diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml
index 6b53151..66b8c3d 100644
--- a/.github/workflows/testsuite.yml
+++ b/.github/workflows/testsuite.yml
@@ -78,7 +78,7 @@ jobs:
         run: ./bootstrap
 
       - name: Configure
-        run: ./configure
+        run: ./configure --enable-dns-tests
 
       - name: Build
         run: make
diff --git a/ChangeLog.sed b/ChangeLog.sed
index 2c13aa4..15eb1da 100644
--- a/ChangeLog.sed
+++ b/ChangeLog.sed
@@ -16,6 +16,9 @@
 /^82eae3a57cf5d9696e56d91a445a776383ba943a#/,/^[[:xdigit:]]{40}#/s/testsing/testing/
 /^3cd8178c85bcad723446f5ca0340f327dc86d139#/,/^[[:xdigit:]]{40}#/s/synonims/synonyms/
 /^2136f919ac05792f45491a13f696d9b23736c132#/,/^[[:xdigit:]]{40}#/s/signture/signature/
+/^45fb45aa690d7e2cd4ec7d7391beb67610cdb276#/,/^[[:xdigit:]]{40}#/s/woul\b/would/
+/^5f586916af6d4cabadcec48d34108978c15b836f#/,/^[[:xdigit:]]{40}#/s/Assing/Assign/
+/^b9bca1ba5ae0cec6d42fc31609a981fb2263e261#/,/^[[:xdigit:]]{40}#/s/dunamic/dynamic/
 
 # Remove commit hashes.  The file must end with this rule.
 s/^[[:xdigit:]]{40}#//
diff --git a/NEWS b/NEWS
index 9ca621c..1d4995b 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-Pound -- history of user-visible changes. 2024-10-13
+Pound -- history of user-visible changes. 2024-10-30
 See the end of file for copying conditions.
 
 Pound is a continuation of the software originally developed by
@@ -6,6 +6,40 @@ Robert Segall at Apsis GmbH, which was officially discontinued
 on 2022-09-19.  See the README file for details.
 
 Please send pound bug reports to <gray@gnu.org>
+
+Version 4.14.91 (git)
+
+* New configuration statement: IgnoreSRVWeight
+
+Instructs pound to ignore weight value of an SRV record when
+generating new backend from it.  Priority of the generated backend is
+copied from its matrix backend.
+
+* New configuration statement: OverrideTTL
+
+When used with dynamic backends, instructs pound to use the supplied
+value as TTL, instead of the one returned in DNS response.
+
+* Load balancing code revisited
+
+Removed arbitrary limit on backend priority value.  The allowed range
+is now 1..65535.
+
+Remove priority mapping for SRV-generated backends.  SRV weights are
+assigned to backend priorities verbatim.
+
+* Fix access to freed memory in session handling code.
+
+* Improve testsuite
+
+Check for missing perl modules and skip tests if needed.
+
+DNS-based tests are disabled by default, due to their experimental
+nature.  Use --enable-dns-tests to enable them.
+
+The poundharness.pl script runs a self-test when invoked with the
+--fakedns option, to avoid spurious test failures.
+
 
 Version 4.14, 2024-10-13
 
@@ -17,24 +51,24 @@ symbolic host name in its "Address" statement and add the "Resolve"
 statement with one of the following values:
 
   first     Resolve the symbolic host name and use first IP from
-            the DNS response as the address of the created dynamic
+	    the DNS response as the address of the created dynamic
 	    backend.  Thus, at most one dynamic backend will be
-            created.
+	    created.
 
   all       Resolve the symbolic host name and create one backend for
-            each address from the DNS response.  This enables load
-            balancing between created backends.  Each backend will be
+	    each address from the DNS response.  This enables load
+	    balancing between created backends.  Each backend will be
 	    assigned the same priority.
 
   srv       Obtain SRV records for the host name and use them to
-            generate regular backends.  Each record produces new
+	    generate regular backends.  Each record produces new
 	    dynamic backend of "Resolve all" type, which creates
 	    regular backends as described above.  The weight field
 	    of the SRV record is mapped to the priority field of each
 	    generated backend.  The SRV priority field determines
 	    the balancing group (see below) where the backend will
 	    be hosted.
-  
+
 By default, both IPv4 and IPv6 addresses are looked for.  You can
 select the specific address family using the "Family" statement.  Its
 allowed values are:
@@ -65,34 +99,34 @@ The "Resolver" section allows you to control how DNS lookups are
 performed.  It can contain the following directives:
 
   CNAMEChain          (integer) Maximum allowed length of a "CNAME
-                      chain".  CNAME chains are formed by DNS CNAME
-                      records pointing to another CNAME.  Although
+		      chain".  CNAME chains are formed by DNS CNAME
+		      records pointing to another CNAME.  Although
 		      prohibited by the RFC, such usage occurs
-                      sometimes in the wild.  By default, pound does
+		      sometimes in the wild.  By default, pound does
 		      not accept CNAME chains.  If you work with a
 		      nameserver that uses them, set this statement
 		      to a small integer value, defining maximum
-                      number of CNAMEs in the chain that pound will
+		      number of CNAMEs in the chain that pound will
 		      accept.  The value of 2 or 3 should suffice in
 		      most cases.
 
   ConfigFile          (string) Name of the resolver configuration file.
-                      Default is "/etc/resolv.conf".
+		      Default is "/etc/resolv.conf".
 
   ConfigText
     ...
   End                 The material within this section is read
-                      verbatim and used as the content of the resolver
+		      verbatim and used as the content of the resolver
 		      configuration file.
 
-                      If both ConfigFile and ConfigText are used, the
+		      If both ConfigFile and ConfigText are used, the
 		      last statement used wins.
 
   Debug               (boolean) Whether to enable DNS debugging info.
 
   RetryInterval       (integer) Interval in seconds, after which to
-                      retry failed DNS queries or queries that
-                      returned no RRs.  This value is used unless the
+		      retry failed DNS queries or queries that
+		      returned no RRs.  This value is used unless the
 		      backend defines its own retry interval value.
 
 Dynamic backends can be controlled using poundctl.  For example,
@@ -100,11 +134,11 @@ consider the following output from "poundctl list":
 
   1. Listener http://192.0.2.1:80 enabled
       0. Service active (5)
-          0. matrix "be0.example.com" 2 0 active
-          1. backend http 198.51.100.15:8081 5 alive active
+	  0. matrix "be0.example.com" 2 0 active
+	  1. backend http 198.51.100.15:8081 5 alive active
 	  2. backend http 203.0.113.121:8081 5 alive active
-          3. backend http 192.0.2.203:8081 5 alive active
-	       
+	  3. backend http 192.0.2.203:8081 5 alive active
+
 The backend 0 ("matrix") refers to the "Backend" statement in the
 configuration file that produced the other three dynamic backends.
 Disabling it (poundctl disable /1/0/0) causes the dynamic ones to
@@ -125,7 +159,7 @@ the output from configure will end with the following status line:
   *******************************************************************
 
 When compiled with the dynamic backend support, output of "pound -V"
-will contain the following line in the "Built-in defaults" section: 
+will contain the following line in the "Built-in defaults" section:
 
   Dynamic backends:           enabled
 
@@ -150,7 +184,7 @@ backends of "srv" resolve type (see above).
 * Emergency backends
 
 Any number of emergency backends can be defined.  Usual request
-balancing algorightm applies when selecting an emergency backend.
+balancing algorithm applies when selecting an emergency backend.
 
 All statements valid within a "Backend" section are also valid within
 an emergency backend declaration.
@@ -188,7 +222,7 @@ http://192.0.2.1:3434:
       Address 192.0.2.1
       Port 3434
       Service
-          ACL "secure"
+	  ACL "secure"
 	  Control
       End
   End
@@ -203,13 +237,13 @@ interface.  First of all, the -s option accepts URL as its argument:
 Additionally, the following new options are implemented:
 
   -C FILE      Load CA certificates from FILE.  If FILE is a
-               directory, all PEM files will be loaded from it.
+	       directory, all PEM files will be loaded from it.
   -K FILE      Load client certificate and key from FILE.  During TLS
-               handshake, send them to the peer for authentication.
-  -k           Insecure mode: disable peer verification.	       
+	       handshake, send them to the peer for authentication.
+  -k           Insecure mode: disable peer verification.
   -S NAME      Take settings for server NAME from the poundctl
-               configuration file (see below).
-	       
+	       configuration file (see below).
+
 ** .poundctl
 
 The file ".poundctl" in user home directory provides configuration
diff --git a/configure.ac b/configure.ac
index 6478d91..986f972 100644
--- a/configure.ac
+++ b/configure.ac
@@ -15,7 +15,7 @@
 # You should have received a copy of the GNU General Public License
 # along with pound.  If not, see <http://www.gnu.org/licenses/>.
 AC_PREREQ([2.71])
-AC_INIT([pound],[4.14],[gray@gnu.org],
+AC_INIT([pound],[4.14.91],[gray@gnu.org],
 	[pound],[https://github.com/graygnuorg/pound])
 AC_CONFIG_AUX_DIR([build-aux])
 AC_CONFIG_SRCDIR([src/pound.c])
@@ -64,6 +64,30 @@ if test $status_dynamic_backends != no; then
 fi
 AM_CONDITIONAL([COND_DYNAMIC_BACKENDS], [test $status_dynamic_backends = yes])
 
+AC_ARG_ENABLE([dns-tests],
+ [AS_HELP_STRING([--enable-dns-tests],
+                 [enable DNS-based dynamic backend tests])],
+ [if test "${status_dynamic_backends}${enableval}" = yesyes; then
+    status_dns_tests=yes
+  else
+    status_dns_tests=no
+  fi
+ ],
+ [status_dns_tests=no])
+
+if test $status_dns_tests = yes; then
+  AC_COMPILE_IFELSE(
+        [AC_LANG_PROGRAM([
+	      #include <sys/syscall.h>
+	      int x[] = { SYS_connect, SYS_sendto, SYS_recvfrom };
+	   ],
+ 	   [[void main(){}]])],
+	 [],
+	 [status_dns_tests=no])
+fi
+
+AM_CONDITIONAL([COND_BUILD_FAKEDNS], [test "$status_dns_tests" = yes])
+
 AC_SUBST(SSL_CPPFLAGS)
 AC_SUBST(SSL_LDFLAGS)
 AC_ARG_WITH([ssl],
@@ -212,6 +236,7 @@ Regular expressions ........................... POSIX$status_pcre
 Memory allocator .............................. $memory_allocator
 Early pthread_cancel probe .................... $status_pthread_cancel_probe
 Dynamic backends .............................. $status_dynamic_backends
+Test dynamic backends ......................... $status_dns_tests
 *******************************************************************
 
 EOF
@@ -229,6 +254,7 @@ else
   status_pthread_cancel_probe=no
 fi
 status_dynamic_backends=$status_dynamic_backends
+status_dns_tests=$status_dns_tests
 ])
 
 AC_CONFIG_TESTDIR(tests)
diff --git a/doc/pound.8 b/doc/pound.8
index ca93e2c..b1c846c 100644
--- a/doc/pound.8
+++ b/doc/pound.8
@@ -14,7 +14,7 @@
 .\"
 .\" You should have received a copy of the GNU General Public License
 .\" along with pound.  If not, see <http://www.gnu.org/licenses/>.
-.TH POUND 8 "October 6, 2024" "pound" "System Manager's Manual"
+.TH POUND 8 "October 30, 2024" "pound" "System Manager's Manual"
 .SH NAME
 pound \- HTTP/HTTPS reverse-proxy and load-balancer
 .SH SYNOPSIS
@@ -152,24 +152,18 @@ identified either by 0-based ordinal number within the configuration
 Backends are identified by their ordinal number within service.
 .SH REQUEST BALANCING
 \fILoad balancing strategy\fR defines algorithm used to distribute
-incoming requests between multiple regular backends.  Two such
-strategies are implemented:
-.TP
-.B Weighted Random Balancing
-This is the default strategy.  Each backend is assigned a numeric
+incoming requests between multiple regular backends.  Each backend
+is assigned a
 .I priority
-between 0 and 9 (inclusive).  The backend to use for each request is
-determined at random taking into account backend priorities, so that
-backends with numerically greater priorities have proportionally greater
-chances of being selected than the ones with lesser priorities.  The
-share of requests a backend handles can be estimated as:
+-- a positive number indicating its relative weight among other backends.
+The share of requests a backend handles can be estimated as:
 .IP
 .RS
 .EX
 .BR "Pi / S(P)" ,
 .EE
 .RE
-.IP
+.PP
 where
 .B Pi
 is priority of the backend with index
@@ -177,28 +171,25 @@ is priority of the backend with index
 and
 .B S(P)
 is sum of all priorities.
+.PP
+Two balancing strategies are implemented:
+.TP
+.B Weighted Random Balancing
+This is the default strategy.  The backend to use for each request is
+determined at random taking into account backend priorities, so that
+backends with numerically greater priorities have proportionally
+greater chances of being selected than the ones with lesser priorities.
 .TP
 .B Interleaved Weighted Round Robin Balancing
-Requests are assigned to each backend in turn.  Backend priorities, or
-.I weights
-are used to control the share of requests handled by each backend.
-The greater the weight, the more requests will be sent to this backend.
-In general, the share of requests assigned to a backend is calculated
-by the following relation:
-.IP
-.RS
-.EX
-.BR "(Pi + 1) / (N + S(P))" ,
-.EE
-.RE
+This strategy cycles over all active backends, considering each one in
+its turn.  An integer ordinal number is assigned to each round, which
+is incremented (modulo number of backends) each time a new round is
+started.  A backend is assigned a request only if its priority is
+greater than the round number.
 .IP
-where
-.B N
-is the number of backends,
-.B Pi
-and
-.B S(P)
-as discussed above.
+This strategy offers several advantages compared with the previous
+one.  First, it results in a more even distribution of the
+requests.  Secondly, the resulting distribution is predictable.
 .PP
 Within each \fBService\fR, multiple backends are grouped in
 .IR "balancer groups" .
@@ -216,7 +207,7 @@ assigned to the balancer group 0.  These are backends used during
 normal operation.  Backends declared using the \fBEmergency\fR keyword
 are assigned to the balancer group 65535.  These backends form a pull
 of \fIhigh availability\fR backends, which will be tried only if all
-of the normal backends fail. 
+of the normal backends fail.
 .PP
 More backend groups can be added using dynamic backends, discussed
 below.
@@ -764,7 +755,7 @@ or
 .B perl
 select Perl-compatible regular expressions.  The latter requires
 compile-time support.  The selected regular expression type remains in
-effect until next 
+effect until next
 .B RegexType
 statement or end of the configuration file, whichever occurs first.
 .SS Control socket
@@ -1619,7 +1610,7 @@ Enables a special \fBcontrol backend\fR -- a management interface that
 returns information about the running \fBpound\fR server, makes it
 possible to change state of configured listeners, services and
 backends, and provides other management facilities.  The management
-interface is discussed in detail in 
+interface is discussed in detail in
 section
 .BR "Control socket" ,
 above.  The \fBControl\fR backend provides remote access to this
@@ -1632,7 +1623,7 @@ ListenHTTPS
     Address 192.0.2.1
     Port 443
     Cert "/etc/ssl/priv/example.pem"
-        
+
     Service
         Not BasicAuth "pound/htpasswd"
         Rewrite response
@@ -1937,9 +1928,21 @@ This directive may appear only after the
 directive.
 .TP
 \fBPriority\fR \fIn\fR
-The priority of this backend (between 1 and 9, 5 is default). Higher priority
-backends will be used more often than lower priority ones, so you should
-define higher priorities for more capable servers.
+The priority of this backend. Higher priority backends will be used
+more often than lower priority ones, so you should define higher
+priorities for more capable servers. Exact scheduling and allowed
+values for this keyword depend on the load balancing strategy in use
+(see the section
+.BR "REQUEST BALANCING" ,
+above).  For
+.IR "weighted random balancing" ,
+allowed values for
+.B Priority
+are between 0 and 9, inclusive.  For
+.IR "interleaved weighted round robin balancing" ,
+allowed range is 0 to 65535, inclusive.
+.IP
+Default value is 5.
 .TP
 \fBTimeOut\fR \fIn\fR
 Override the global
@@ -1977,7 +1980,7 @@ backend.  Thus, at most one dynamic backend will be produced.
 .B all
 Resolve the symbolic host name from the \fBAddress\fR directive and
 create one backend for each address from the DNS response.  This
-enables load balancing between created backends.  Each backend will be 
+enables load balancing between created backends.  Each backend will be
 assigned the same priority.
 .TP
 .B srv
@@ -2005,6 +2008,19 @@ Use only IPv4 addresses (\fBA\fR DNS RRs).
 Use only IPv6 addresses (\fBAAAA\fR DNS RRs).
 .RE
 .TP
+\fBIgnoreSRVWeight \fIbool\fR
+When using \fBSRV\fR records, ignore their \fIweight\fR fields and
+assign priorities for the generated backends from the priority of
+the producing backend.
+
+This directive is valid when used together with
+.BR "Resolve srv" .
+.TP
+.BI OverrideTTL " N"
+Query DNS each \fIN\fR seconds for possible changes in configuration
+of the existing dynamic backends.  If this statement is not used, the
+TTL value returned with the DNS response will be used instead.
+.TP
 .BI RetryInterval " N"
 Retry failed DNS queries each \fIN\fR seconds.  Default is 600.
 .SS Global and per-service backends
diff --git a/doc/pound.texi b/doc/pound.texi
index 7961b29..efc1743 100644
--- a/doc/pound.texi
+++ b/doc/pound.texi
@@ -95,6 +95,11 @@ This edition of the @cite{Pound Manual}, last updated
 * Time and Date Formats::
 * GNU Free Documentation License::
 * Index::
+@ifset WEBDOC
+@ifhtml
+* This Manual in Other Formats::
+@end ifhtml
+@end ifset
 
 @detailmenu
  --- The Detailed Node Listing ---
@@ -1439,9 +1444,9 @@ End
   When several backends are defined in a service, incoming requests
 will be distributed among them.  This process is called @dfn{balancing}.
 By default, requests are distributed equally.  This can be changed by
-assigning them a @dfn{priority} -- a decimal number which controls a
+assigning them a @dfn{priority} -- a positive integer which assigns a
 relative weight of the given backend in the distribution algorithm.
-The bigger the priority is, the more requests this backend gets from
+The greater the priority is, the more requests this backend gets from
 the total flow.
 
 @cindex balancing strategy
@@ -1455,40 +1460,36 @@ weighted round robin balancing}.
 @cindex Weighted random balancing
 @item Weighted Random Balancing
 
-This is the default strategy.  Each backend is assigned a numeric
-priority between 0 and 9, inclusive.  The backend to use for each
-request is determined at random taking into account backend
+This is the default strategy.  The backend to use for each
+request is determined at random, taking into account backend
 priorities, so that backends with numerically greater priorities have
 proportionally greater chances of being selected than the ones with
 lesser priorities.
 
-The share of requests a backend receives can be estimated as:
-
-@example
-@math{P@sub{i} / S(P)}
-@end example
-
-@noindent
-where @math{P@sub{i}} is the priority of the backend with index
-@math{i}, and @math{S(P)} is the sum of all priorities.
-
 @cindex Interleaved weighted round robin balancing
 @item Interleaved Weighted Round Robin Balancing
 
-Requests are assigned to each backend in turn.  Backend priorities, or
-weights, are used to control the share of requests received by each
-backend.  The greater the weight, the more requests will be sent to
-this backend.  In general, the share of requests assigned to a backend
-is calculated by the following relation:
+This strategy cycles over all active backends, considering each one in
+its turn.  An integer ordinal number is assigned to each round, which
+is incremented (modulo number of backends) each time a new round is
+started.  A backend is assigned a request only if its priority is
+greater than the round number.
+
+This strategy offers several advantages compared with the previous
+one.  First, it results in a more even distribution of the
+requests.  Secondly, the resulting distribution is predictable.
+@end table
+
+  Overall, the share of requests a given backend receives can be
+estimated as:
 
 @example
-@math{(P@sub{i} + 1) / (N + S(P))}
+@math{P@sub{i} / S(P)}
 @end example
 
 @noindent
-where @math{N} is total number of backends, and @math{P@sub{i}} and
-@math{S(P)} are as discussed above.
-@end table
+where @math{P@sub{i}} is the priority of the backend with index
+@math{i}, and @math{S(P)} is the sum of all priorities.
 
   Weighted random balancing is used by default.  Each backend gets the
 default priority 5, unless another value is expressly assigned using
@@ -1678,23 +1679,29 @@ in the next section.
 
 @node Dynamic backends
 @section Dynamic backends
+@cindex dynamic backends
+@cindex backends, dynamic
   Normally, when a backend is declared in configuration file, the
 value supplied with its @code{Address} statement is resolved at once
 and the resulting IP is associated with the backend.  Such backends
 are @dfn{static}.  You can also instruct @command{pound} to resolve
 the hostname and create as many backends as are the IP addresses it
-resolves to.  Such backends are called @dfn{dynamic}.  When a dynamic
+resolves to.  Such backends are called @dfn{dynamic}.@footnote{Support for
+dynamic backends is enabled at compile time and requires the
+@uref{http://www.gnu.org/software/adns, GNU adns library}.  If unsure
+whether your @command{pound} binary includes it, inspect the
+@command{pound -V} output.}  When a dynamic
 backend is requested, the @code{Backend} statement defines a
 @dfn{matrix} which will be used as a template to generate actual
 backends from the data in DNS.  The TTL value of the DNS record (or
-records) will define the time to live of the generated backends.  When
+records) defines minimum time to live of the generated backends.  When
 it expires, DNS will be queried again and backends recreated
-according to the changes in its responses.@footnote{Support for
-dynamic backends is enabled at compile time and requires the
-@uref{http://www.gnu.org/software/adns, GNU adns library}.  If unsure
-whether your @command{pound} binary includes it, inspect the
-@command{pound -V} output.}
+according to the changes in its responses@footnote{If necessary, the TTL
+value can be changed using the @code{OverrideTTL} configuration
+directive.  @xref{Backend, OverrideTTL}.}.
 
+@cindex @code{A} record, DNS
+@cindex @code{AAAA} record, DNS
   In the simplest case, a dynamic backend can be created using
 @code{A} or @code{AAAA} DNS records.  If several records correspond to
 the hostname defined in the @code{Address} statement, @command{pound}
@@ -1703,13 +1710,23 @@ them in round-robin fashion and use it to create a backend.  Rest of
 backend parameters (port value, priority, etc.) will be taken from its
 matrix declaration.
 
+@cindex @code{SRV} record, DNS
+@cindex weight, @code{SRV} record
+@cindex priority, @code{SRV} record
   A more advanced way to create dynamic backends is using @code{SRV}
 records.  Each @code{SRV} record has four associated parameters: priority,
-weight, target and port.  The priority value defines the @command{pound}
-balancer group where to put generated backends.  Weight is assigned
-as the priority for each generated backend.  Finally, target is used
-as hostname to obtain IP addresses for the backends.  For example,
-suppose the following DNS records are defined:
+weight, target and port.  The @dfn{priority} value defines the @command{pound}
+balancer group where to put generated backends.  @dfn{Weight} is assigned
+as the priority for each generated backend@footnote{Notice, that the
+@code{SRV} terminology differs from what @command{pound} uses.  What
+is called @i{priority} in @code{SRV} defines the @dfn{weight} of the
+backend group for @command{pound}, whereas what is called @i{weight}
+in @code{SRV} defines the @dfn{priority} of a backend within a group.
+This confusion is due to historical reasons, and it would be hard to
+avoid it without breaking backward compatibility.}.
+Finally, @dfn{target} is used as hostname to obtain IP addresses for
+the backends.  For example, suppose the following DNS records are
+defined:
 
 @example
 @group
@@ -1750,6 +1767,23 @@ Emergency
 End
 @end example
 
+@cindex @code{SRV} record, 0 weight
+  @emph{Notice}, that an SRV record can have a weight of 0.  This will
+translate to @command{pound} backend priority 0, which is normally not
+allowed (@pxref{Balancer}).  According to RFC 2782, this is the
+preferred way of defining weights where ``there isn't any server
+selection to do'', whereas ``[i]n the presence of records containing
+weights greater than 0, records with weight 0 should have a very small
+chance of being selected''.  @command{Pound} handles this as follows:
+if all @code{SRV} records have zero weight, priority of the resulting
+backends is set to 1.  This makes sure the backend selection algorithm
+works and gives each backend equal chance of being selected. This
+translation does not take place, however, if at least one of
+@code{SRV} records has a weight greater than 0.  Another way to handle
+such records is using the @code{IgnoreSRVWeight} configuration
+statement, which is discussed below.
+
+@kwindex Resolve
   To declare backend as dynamic, use a symbolic host name in its
 @code{Address} statement and add the @code{Resolve} statement with one
 of the following values:
@@ -1775,18 +1809,22 @@ field of each generated backend.  The priority field determines the
 balancer group where the backend will be hosted.
 @end deffn
 
+@kwindex Family
 When resolving hostnames, both IPv4 and IPv6 addresses are looked for.
 You can select the specific address family using the @code{Family}
 statement.  Its allowed values are:
 
 @table @code
 @item any
+@kwindex any, @code{Family} statement
 Use all address families available.  This is the default.
 
 @item inet
+@kwindex inet, @code{Family} statement
 Use only IPv4 addresses (@code{A} DNS records).
 
 @item inet6
+@kwindex inet6, @code{Family} statement
 Use only IPv6 addresses (@code{AAAA} DNS records).
 @end table
 
@@ -1796,15 +1834,56 @@ Use only IPv6 addresses (@code{AAAA} DNS records).
 @group
 Backend
         Address "_proxy._tcp.example.org"
-	Resolve srv
-	Family any
+        Resolve srv
+        Family any
 End
 @end group
 @end example
 
-Notice, that you need not supply @code{Port} or @code{Priority} values
-for the dynamic backends of @code{srv} resolve type.
+@kwindex IgnoreSRVWeight
+@cindex @code{SRV} records, overriding weight
+@cindex weight override, @code{SRV} records
+Notice, that you need not supply @code{Port} statement for dynamic
+backends of @code{srv} resolve type.  Normally, you don't need to
+supply @code{Priority}, either.  However, you may do so, if you wish
+to override the weight settings from @code{SRV} records.  In that
+case, inform @code{pound} about your decision using the following
+statement in @code{Backend} section:
+
+@example
+IgnoreSRVWeight true
+@end example
 
+There are several possible scenarios when this may be needed.  One of
+them is when all @code{SRV} records have weight of 0 (see above) and your
+configuration defines one or more regular backends in the same
+balancer group as backends generated by those records.  In that case,
+the default priority assigned by @command{pound} to those generated
+backends (1) may prove to be too low for adequate balancing.  Using
+@code{IgnoreSRVWeight} allows you to fix that.  For example:
+
+@example
+Service
+        Backend
+                Address 192.0.2.15
+                Port 80
+                Priority 8
+        End
+        Backend
+                Address "_proxy._tcp.example.org"
+                Resolve srv
+                Family any
+                IgnoreSRVWeight true
+                Priority 5
+        End
+End
+@end example
+
+In this example, backends generated by @code{SRV} records for
+domain @samp{_proxy._tcp.example.org} will be assigned priority 5,
+no matter what the actual value of @code{SRV} weight field.
+
+@kwindex RetryInterval
 Dynamic backends will be updated periodically, when the TTL of the
 corresponding DNS records expires.  If the hostname cannot be resolved
 or a DNS failure occurs, next update will be scheduled in 600
@@ -2876,7 +2955,7 @@ ListenHTTPS
     Port 443
     Cert "/etc/ssl/priv/example.pem"
     Disable TLSv1
-    
+
     Service
         Not BasicAuth "pound/htpasswd"
         Rewrite response
@@ -2884,7 +2963,7 @@ ListenHTTPS
         End
         Error 401
     End
-    
+
     Service
         ACL "secure"
         Control
@@ -4288,9 +4367,7 @@ Sets numeric priority for this backend.  Priorities are used to
 control probability of receiving a request for handling in case of
 multiple backends.  @xref{Balancer}, for a detailed discussion.
 
-Allowed values for @var{n} depend on the balancing algorithm in use.
-For random balancing, allowed values are 0 to 9.  For @sc{iwrr},
-allowed values are between 0 and 65535.
+Allowed values for @var{n} are 1 to 65535.
 @end deffn
 
 Following directives configure @dfn{dynamic backends}.  @pxref{Dynamic
@@ -4311,6 +4388,26 @@ Use only IPv6 addresses.
 @end table
 @end deffn
 
+@deffn {Backend directive} IgnoreSRVWeight @var{bool}
+When using @code{SRV} records, ignore their @i{weight} fields.
+Instead, copy priority of the producing backend to generated
+backends.
+
+This directive is valid when used together with @code{Resolve srv}
+(see below).
+@end deffn
+
+@deffn {Backend directive} OverrideTTL @var{n}
+This directive configures update interval (in seconds) for dynamic
+backends (@pxref{Dynamic backends}).  At the end of each update
+interval, the DNS be queried and dynamic backends reconfigured (if
+needed) according to its responses.  By default, the TTL value
+obtained from previous DNS response is used.
+
+To configure the interval to retry failed DNS lookups, see
+@code{RetryInterval}, below.
+@end deffn
+
 @deffn {Backend directive} Resolve @var{type}
 Declares a dynamic backend.  Valid values
 for @var{type} are
@@ -4630,6 +4727,7 @@ instead (@pxref{conditional-option}).
 
 @node resolver
 @section Resolver
+@kwindex Resolver
   The @code{Resolver} section controls DNS lookups for dynamic backend
 generation (@pxref{Dynamic backends}).  It can contain the following
 directives:
@@ -4664,7 +4762,7 @@ Resolver
                 nameserver 192.0.2.1
                 nameserver 192.0.2.4
         End
-End        
+End
 @end group
 @end example
 
@@ -4689,7 +4787,7 @@ the running instance and allows you to change some of them.
 
   The program communicates with the running @command{pound} daemon via
 a UNIX socket or remotely, via HTTP or HTTPS.  The URL of the
-communication socket can be given explicitely by the @option{-s}
+communication socket can be given explicitly by the @option{-s}
 command line option, or obtained from @command{poundctl} or
 @command{pound} configuration files.
 
@@ -4877,7 +4975,7 @@ statement} occupies a single line and consists of a keyword
 @dfn{section} is a compound statement that encloses other statements
 and sections.  Sections begin with a keyword, optionally followed by
 arguments, and end with a word @code{End} on a line by itself.  All
-keywords are case-insensitive. 
+keywords are case-insensitive.
 
 The following keywords are available in the global scope:
 
@@ -4951,7 +5049,7 @@ End
 where @var{name} is a unique name assigned to that server.  It will be
 used as argument to the @option{-S} option to identify it.  The
 ellipsis denotes one or more of the following statements:
-  
+
 @deffn {poundctl Server} URL @var{url}
 Sets the URL of the @command{pound} management socket for that server.
 The value is either the file name of the UNIX socket file, or a remote
diff --git a/doc/poundctl.8 b/doc/poundctl.8
index 72c62c0..c8519e1 100644
--- a/doc/poundctl.8
+++ b/doc/poundctl.8
@@ -14,7 +14,7 @@
 .\"
 .\" You should have received a copy of the GNU General Public License
 .\" along with pound.  If not, see <http://www.gnu.org/licenses/>.
-.TH POUNDCTL 8 "October 3, 2024" "poundctl" "System Manager's Manual"
+.TH POUNDCTL 8 "October 13, 2024" "poundctl" "System Manager's Manual"
 .SH NAME
 poundctl \- control the pound daemon
 .SH SYNOPSIS
@@ -288,7 +288,7 @@ Operate on server defined in
 file, section \fBServer \(dq\fINAME\fB\(dq\fR.
 .TP
 \fB\-s \fISOCKET\fR
-Sets control socket pathname.  \fISOCKET\fR can aslo be a URL in the
+Sets control socket pathname.  \fISOCKET\fR can also be a URL in the
 form:
 .IP
 {\fBhttp\fR|\fBhttps\fR}\fB://\fR[\fIUSER\fR[\fB:\fIPASS\fR]\fB@\fR]\fIHOSTNAME\fR[\fB:\fIPORT\fT][\fB/\fIPATH\fR]
diff --git a/doc/webdoc.init b/doc/webdoc.init
index 543db5e..bac7865 100644
--- a/doc/webdoc.init
+++ b/doc/webdoc.init
@@ -5,9 +5,6 @@ set_from_init_file('EXTRA_HEAD', qq{
 });
 set_from_init_file('CSS_LINES', '');
 set_from_init_file('SHOW_TITLE',undef);
-if ($Texinfo::Convert::HTML::VERSION < 6.8) {
-    set_from_init_file('SHOW_MENU',undef);
-}
 # set_from_init_file('TOP_FILE','Pound.html');
 # set_from_init_file('TOP_BUTTONS', undef);
 
diff --git a/src/config.c b/src/config.c
index 61c4fb1..4b90777 100644
--- a/src/config.c
+++ b/src/config.c
@@ -67,6 +67,31 @@ openssl_error_at_locus_range (struct locus_range const *loc,
 #define conf_openssl_error(file, msg)				\
   openssl_error_at_locus_range (last_token_locus_range (), file, msg)
 
+BACKEND *
+backend_create (BACKEND_TYPE type, int prio, struct locus_range *loc)
+{
+  BACKEND *be = calloc (1, sizeof (*be));
+  if (be)
+    {
+      be->be_type = type;
+      be->priority = prio;
+      pthread_mutex_init (&be->mut, &mutex_attr_recursive);
+      if (loc)
+	be->locus = *loc;
+      be->refcount = 1;
+    }
+  return be;
+}
+
+static BACKEND *
+xbackend_create (BACKEND_TYPE type, int prio, struct locus_range *loc)
+{
+  BACKEND *be = backend_create (type, prio, loc);
+  if (!be)
+    xnomem ();
+  return be;
+}
+
 /*
  * Named backends
  */
@@ -266,7 +291,7 @@ assign_CONTENT_LENGTH (void *call_data, void *section_data)
     }
   *(CONTENT_LENGTH *)call_data = n;
   return 0;
-}  
+}
 
 static int
 assign_cert (void *call_data, void *section_data)
@@ -274,7 +299,7 @@ assign_cert (void *call_data, void *section_data)
   X509 **x509_ptr = call_data, *cert;
   struct token *tok;
   FILE *fp;
-  
+
   if ((tok = gettkn_expect (T_STRING)) == NULL)
     return CFGPARSER_FAIL;
 
@@ -710,7 +735,7 @@ backend_parse_cert (void *call_data, void *section_data)
   BACKEND *be = call_data;
   struct token *tok;
   char *filename;
-  
+
   if (be->v.mtx.ctx == NULL)
     {
       conf_error ("%s", "HTTPS must be used before this statement");
@@ -741,7 +766,7 @@ backend_parse_cert (void *call_data, void *section_data)
       return CFGPARSER_FAIL;
     }
   free (filename);
-  
+
   return CFGPARSER_OK;
 }
 
@@ -767,7 +792,7 @@ backend_assign_ciphers (void *call_data, void *section_data)
 static int
 backend_assign_priority (void *call_data, void *section_data)
 {
-  return cfg_assign_int_range (call_data, 0, -1);
+  return cfg_assign_int_range (call_data, 1, -1);
 }
 
 static int
@@ -876,6 +901,16 @@ static CFGPARSER_TABLE backend_parsetab[] = {
     .parser = assign_resolve_mode,
     .off = offsetof (BACKEND, v.mtx.resolve_mode)
   },
+  {
+    .name = "IgnoreSRVWeight",
+    .parser = cfg_assign_bool,
+    .off = offsetof (BACKEND, v.mtx.ignore_srv_weight)
+  },
+  {
+    .name = "OverrideTTL",
+    .parser = cfg_assign_timeout,
+    .off = offsetof (BACKEND, v.mtx.override_ttl)
+  },
   {
     .name = "RetryInterval",
     .parser = cfg_assign_timeout,
@@ -977,13 +1012,10 @@ parse_backend_internal (CFGPARSER_TABLE *table, POUND_DEFAULTS *dfl,
   BACKEND *be;
   struct locus_range range;
 
-  XZALLOC (be);
-  be->be_type = BE_MATRIX;
+  be = xbackend_create (BE_MATRIX, 5, NULL);
   be->v.mtx.to = dfl->be_to;
   be->v.mtx.conn_to = dfl->be_connto;
   be->v.mtx.ws_to = dfl->ws_to;
-  be->priority = 5;
-  pthread_mutex_init (&be->mut, NULL);
 
   if (parser_loop (table, be, dfl, &range))
     return NULL;
@@ -1012,12 +1044,9 @@ parse_backend (void *call_data, void *section_data)
 
       range.beg = beg;
 
-      XZALLOC (be);
-      be->be_type = BE_BACKEND_REF;
+      be = xbackend_create (BE_BACKEND_REF, -1, NULL);
       be->v.be_name = xstrdup (tok->str);
-      be->priority = -1;
       be->disabled = -1;
-      pthread_mutex_init (&be->mut, NULL);
 
       if (parser_loop (use_backend_parsetab, be, section_data, &range))
 	return CFGPARSER_FAIL;
@@ -1046,13 +1075,9 @@ parse_use_backend (void *call_data, void *section_data)
   if ((tok = gettkn_expect (T_STRING)) == NULL)
     return CFGPARSER_FAIL;
 
-  XZALLOC (be);
-  be->be_type = BE_BACKEND_REF;
+  be = xbackend_create (BE_BACKEND_REF, 5, &tok->locus);
   be->v.be_name = xstrdup (tok->str);
-  be->locus = tok->locus;
   be->locus_str = format_locus_str (&tok->locus);
-  be->priority = 5;
-  pthread_mutex_init (&be->mut, NULL);
 
   balancer_add_backend (balancer_list_get_normal (bml), be);
 
@@ -1083,12 +1108,7 @@ static int
 parse_control_backend (void *call_data, void *section_data)
 {
   BALANCER_LIST *bml = call_data;
-  BACKEND *be;
-
-  XZALLOC (be);
-  be->be_type = BE_CONTROL;
-  be->priority = 1;
-  pthread_mutex_init (&be->mut, NULL);
+  BACKEND *be = xbackend_create (BE_CONTROL, 1, last_token_locus_range ());
   balancer_add_backend (balancer_list_get_normal (bml), be);
   return CFGPARSER_OK;
 }
@@ -1097,12 +1117,7 @@ static int
 parse_metrics (void *call_data, void *section_data)
 {
   BALANCER_LIST *bml = call_data;
-  BACKEND *be;
-
-  XZALLOC (be);
-  be->be_type = BE_METRICS;
-  be->priority = 1;
-  pthread_mutex_init (&be->mut, NULL);
+  BACKEND *be = xbackend_create (BE_METRICS, 1, last_token_locus_range ());
   balancer_add_backend (balancer_list_get_normal (bml), be);
   return CFGPARSER_OK;
 }
@@ -1655,12 +1670,8 @@ parse_redirect_backend (void *call_data, void *section_data)
       return CFGPARSER_FAIL;
     }
 
-  XZALLOC (be);
+  be = xbackend_create (BE_REDIRECT, 1, &range);
   be->locus_str = format_locus_str (&range);
-  be->be_type = BE_REDIRECT;
-  be->priority = 1;
-  pthread_mutex_init (&be->mut, NULL);
-
   be->v.redirect.status = code;
   be->v.redirect.url = xstrdup (tok->str);
 
@@ -1721,13 +1732,8 @@ parse_error_backend (void *call_data, void *section_data)
 
   range.end = last_token_locus_range ()->end;
 
-  XZALLOC (be);
-  be->locus = range;
+  be = xbackend_create (BE_ERROR, 1, &range);
   be->locus_str = format_locus_str (&range);
-  be->be_type = BE_ERROR;
-  be->priority = 1;
-  pthread_mutex_init (&be->mut, NULL);
-
   be->v.error.status = status;
   be->v.error.text = text;
 
@@ -2549,37 +2555,37 @@ static CFGPARSER_TABLE service_parsetab[] = {
   {
     .name = "Redirect",
     .parser = parse_redirect_backend,
-    .off = offsetof (SERVICE, backends)
+    .off = offsetof (SERVICE, balancers)
   },
   {
     .name = "Error",
     .parser = parse_error_backend,
-    .off = offsetof (SERVICE, backends)
+    .off = offsetof (SERVICE, balancers)
   },
   {
     .name = "Backend",
     .parser = parse_backend,
-    .off = offsetof (SERVICE, backends)
+    .off = offsetof (SERVICE, balancers)
   },
   {
     .name = "UseBackend",
     .parser = parse_use_backend,
-    .off = offsetof (SERVICE, backends)
+    .off = offsetof (SERVICE, balancers)
   },
   {
     .name = "Emergency",
     .parser = parse_emergency,
-    .off = offsetof (SERVICE, backends)
+    .off = offsetof (SERVICE, balancers)
   },
   {
     .name = "Metrics",
     .parser = parse_metrics,
-    .off = offsetof (SERVICE, backends)
+    .off = offsetof (SERVICE, balancers)
   },
   {
     .name = "Control",
     .parser = parse_control_backend,
-    .off = offsetof (SERVICE, backends)
+    .off = offsetof (SERVICE, balancers)
   },
   {
     .name = "Session",
@@ -2636,7 +2642,7 @@ new_service (BALANCER_ALGO algo)
   XZALLOC (svc);
 
   service_cond_init (&svc->cond, COND_BOOL);
-  DLIST_INIT (&svc->backends);
+  DLIST_INIT (&svc->balancers);
 
   svc->sess_type = SESS_NONE;
   pthread_mutex_init (&svc->mut, &mutex_attr_recursive);
@@ -2644,15 +2650,10 @@ new_service (BALANCER_ALGO algo)
 
   DLIST_INIT (&svc->be_rem_head);
   pthread_cond_init (&svc->be_rem_cond, NULL);
-  
+
   return svc;
 }
 
-static int backend_pri_max[] = {
-  [BALANCER_ALGO_RANDOM] = PRI_MAX_RANDOM,
-  [BALANCER_ALGO_IWRR]   = PRI_MAX_IWRR
-};
-   
 static int
 parse_service (void *call_data, void *section_data)
 {
@@ -2663,7 +2664,7 @@ parse_service (void *call_data, void *section_data)
   struct locus_range range;
 
   svc = new_service (dfl->balancer_algo);
-  
+
   tok = gettkn_any ();
 
   if (!tok)
@@ -2689,93 +2690,8 @@ parse_service (void *call_data, void *section_data)
 
   if (parser_loop (service_parsetab, svc, dfl, &range))
     return CFGPARSER_FAIL;
-  else
-    {
-      BALANCER *be_list;
-      unsigned be_count = 0;
-      
-      DLIST_FOREACH (be_list, &svc->backends, link)
-	{
-	  BACKEND *be;	  
-	  int be_class = 0;
-#         define BE_MASK(n) (1<<(n))
-#         define  BX_(x)  ((x) - (((x)>>1)&0x77777777)			\
-			   - (((x)>>2)&0x33333333)			\
-			   - (((x)>>3)&0x11111111))
-#         define BITCOUNT(x)     (((BX_(x)+(BX_(x)>>4)) & 0x0F0F0F0F) % 255)
-	  int n = 0;
-	  int pri_max = backend_pri_max[svc->balancer_algo];
-
-	  be_list->tot_pri = 0;
-	  be_list->max_pri = 0;
-	  DLIST_FOREACH (be, &be_list->backends, link)
-	    {
-	      n++;
-	      if (be->priority > pri_max)
-		{
-		  conf_error_at_locus_range (&be->locus,
-					     "backend priority out of allowed"
-					     " range; reset to max. %d",
-					     pri_max);
-		  be->priority = pri_max;
-		}
-	      be_class |= BE_MASK (be->be_type);
-	      be->service = svc;
-	      if (!be->disabled)
-		{
-		  if (TOT_PRI_MAX - be_list->tot_pri > be->priority)
-		    be_list->tot_pri += be->priority;
-		  else
-		    {
-		      conf_error_at_locus_range (&be->locus,
-						 "this backend overflows the"
-						 " sum of priorities");
-		      return CFGPARSER_FAIL;
-		    }
-		  if (be_list->max_pri < be->priority)
-		    be_list->max_pri = be->priority;
-		}
-	    }
-
-	  if (n > 1)
-	    {
-	      if (be_class & ~(BE_MASK (BE_REGULAR) |
-			       BE_MASK (BE_MATRIX) |
-			       BE_MASK (BE_REDIRECT)))
-		{
-		  conf_error_at_locus_range (&range,
-			  "%s",
-			  BITCOUNT (be_class) == 1
-			    ? "multiple backends of this type are not allowed"
-			    : "service mixes backends of different types");
-		  return CFGPARSER_FAIL;
-		}
-
-	       if (be_class & BE_MASK (BE_REDIRECT))
-		{
-		  conf_error_at_locus_range (&range,
-			  "warning: %s",
-			  (be_class & (BE_MASK (BE_REGULAR) |
-				       BE_MASK (BE_MATRIX)))
-			     ? "service mixes regular and redirect backends"
-			     : "service uses multiple redirect backends");
-		  conf_error_at_locus_range (&range,
-			  "see section \"DEPRECATED FEATURES\" in pound(8)");
-		}
-	    }
-	  
-	  be_count += n;
-	}
 
-      if (be_count == 0)
-	{
-	  conf_error_at_locus_range (&range, "warning: no backends defined");
-	}
-      
-      service_lb_init (svc);
-
-      SLIST_PUSH (head, svc, next);
-    }
+  SLIST_PUSH (head, svc, next);
   svc->locus_str = format_locus_str (&range);
   return CFGPARSER_OK;
 }
@@ -2832,16 +2748,13 @@ parse_acme (void *call_data, void *section_data)
   svc->locus_str = format_locus_str (&range);
 
   /* Create ACME backend */
-  XZALLOC (be);
-  be->be_type = BE_ACME;
+  be = xbackend_create (BE_ACME, 1, &range);
   be->service = svc;
   be->priority = 1;
-  pthread_mutex_init (&be->mut, NULL);
-
   be->v.acme.wd = fd;
 
   /* Register backend in service */
-  balancer_add_backend (balancer_list_get_normal (&svc->backends), be);
+  balancer_add_backend (balancer_list_get_normal (&svc->balancers), be);
   service_recompute_pri_unlocked (svc, NULL, NULL);
 
   /* Register service in the listener */
@@ -2880,7 +2793,7 @@ listener_parse_socket_from (void *call_data, void *section_data)
   struct token *tok;
   struct sockaddr_un *sun;
   size_t len;
-  
+
   if (lst->addr_str || lst->port_str)
     {
       conf_error ("%s", "Duplicate Address or SocketFrom statement");
@@ -3441,7 +3354,7 @@ resolve_listener_address (LISTENER *lst, char *defsrv,
       struct addrinfo hints, *res, *ptr;
       char *service;
       int rc;
-      
+
       memset (&hints, 0, sizeof (hints));
       hints.ai_family = AF_UNSPEC;
       hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
@@ -3505,12 +3418,12 @@ parse_listen_http (void *call_data, void *section_data)
 
   if (resolve_listener_address (lst, PORT_HTTP_STR, &range) != CFGPARSER_OK)
     return CFGPARSER_FAIL;
-  
+
   if (forbid_ssl_usage (&lst->services,
 			"use of SSL features in ListenHTTP sections"
 			" is forbidden"))
     return CFGPARSER_FAIL;
-  
+
   lst->locus_str = format_locus_str (&range);
 
   SLIST_PUSH (list_head, lst, next);
@@ -3672,7 +3585,7 @@ https_parse_cert (void *call_data, void *section_data)
   struct token *tok;
   struct stat st;
   char *certname;
-  
+
   if ((tok = gettkn_expect (T_STRING)) == NULL)
     return CFGPARSER_FAIL;
 
@@ -4113,7 +4026,7 @@ client_cert_cb (X509 *x509, void *data)
   lst->verify = 1;
   return 0;
 }
-  
+
 
 static int
 flush_service_client_cert (LISTENER *lst)
@@ -4200,7 +4113,7 @@ parse_listen_https (void *call_data, void *section_data)
 
   if (flush_service_client_cert (lst) != CFGPARSER_OK)
     return CFGPARSER_FAIL;
-  
+
 #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
   if (!SLIST_EMPTY (&lst->ctx_head))
     {
@@ -4364,17 +4277,13 @@ parse_control_listener (void *call_data, void *section_data)
   SLIST_PUSH (&lst->services, svc, next);
 
   /* Create backend */
-  XZALLOC (be);
-  be->locus = range;
+  be = xbackend_create (BE_CONTROL, 1, &range);
   be->locus_str = format_locus_str (&range);
   be->service = svc;
-  be->be_type = BE_CONTROL;
-  be->priority = 1;
-  pthread_mutex_init (&be->mut, NULL);
   /* Register backend in service */
-  balancer_add_backend (balancer_list_get_normal (&svc->backends), be);
+  balancer_add_backend (balancer_list_get_normal (&svc->balancers), be);
   service_recompute_pri_unlocked (svc, NULL, NULL);
-  
+
   return CFGPARSER_OK;
 }
 
@@ -4496,14 +4405,14 @@ read_resolv_conf (void *call_data, void *section_data)
     }
   return cfg_assign_string_from_file (pstr, section_data);
 }
-  
+
 static int
 read_resolv_text (void *call_data, void *section_data)
 {
   char **pstr = call_data;
   char *str;
   struct token *tok;
-  
+
   if (*pstr)
     {
       conf_error ("%s", "ConfigText statement overrides prior ConfigFile");
@@ -4522,7 +4431,7 @@ read_resolv_text (void *call_data, void *section_data)
 		  token_type_str (tok->type));
       return CFGPARSER_FAIL;
     }
-      
+
   if (cfg_read_to_end (cur_input, &str) == EOF)
     return CFGPARSER_FAIL;
   *pstr = xstrdup (str);
@@ -4541,7 +4450,7 @@ static CFGPARSER_TABLE resolver_parsetab[] = {
   {
     .name = "ConfigText",
     .parser = read_resolv_text,
-  },    
+  },
   {
     .name = "Debug",
     .parser = cfg_assign_bool,
@@ -4915,12 +4824,35 @@ backend_resolve (BACKEND *be)
   return 0;
 }
 
+struct be_setup_closure
+{
+  /* Input */
+  NAMED_BACKEND_TABLE *be_tab;
+  SERVICE *svc;        /* Service. */
+  BALANCER *bal;       /* Balancer. */
+
+  /* Output */
+  int be_count;        /* Number of backends processed. */
+  int be_class;        /* Backend class mask. */
+  int err;             /* Errors encountered or not. */
+};
+
+static void
+be_setup_closure_init (struct be_setup_closure *cp,
+		       NAMED_BACKEND_TABLE *tab,
+		       SERVICE *svc, BALANCER *bal)
+{
+  memset (cp, 0, sizeof *cp);
+  cp->be_tab = tab;
+  cp->svc = svc;
+  cp->bal = bal;
+}
+
 static int
-backend_finalize (BACKEND *be, void *data)
+backend_finalize (BACKEND *be, NAMED_BACKEND_TABLE *tab)
 {
   if (be->be_type == BE_BACKEND_REF)
     {
-      NAMED_BACKEND_TABLE *tab = data;
       NAMED_BACKEND *nb;
 
       nb = named_backend_retrieve (tab, be->v.be_name);
@@ -4996,6 +4928,98 @@ backend_finalize (BACKEND *be, void *data)
     }
   return 0;
 }
+
+#define BE_MASK(n) (1<<(n))
+#define BX_(x)  ((x) - (((x)>>1)&0x77777777)			\
+		 - (((x)>>2)&0x33333333)			\
+		 - (((x)>>3)&0x11111111))
+#define BITCOUNT(x)     (((BX_(x)+(BX_(x)>>4)) & 0x0F0F0F0F) % 255)
+
+static void
+cb_be_setup (BACKEND *be, void *data)
+{
+  struct be_setup_closure *clos = data;
+
+  if (backend_finalize (be, clos->be_tab))
+    {
+      be->disabled = 1;
+      clos->err = 1;
+      return;
+    }
+
+  clos->be_count++;
+  clos->be_class |= BE_MASK (be->be_type);
+  be->service = clos->svc;
+  if (be->priority > PRI_MAX)
+    {
+      conf_error_at_locus_range (&be->locus,
+				 "backend priority out of allowed"
+				 " range; reset to max. %d",
+				 PRI_MAX);
+      be->priority = PRI_MAX;
+    }
+}
+
+static int
+service_finalize (SERVICE *svc, void *data)
+{
+  BALANCER *bal;
+  unsigned be_count = 0;
+  NAMED_BACKEND_TABLE *tab = data;
+
+  DLIST_FOREACH (bal, &svc->balancers, link)
+    {
+      struct be_setup_closure be_setup;
+      bal->algo = svc->balancer_algo;
+      be_setup_closure_init (&be_setup, tab, svc, bal);
+      balancer_recompute_pri_unlocked (bal, cb_be_setup, &be_setup);
+      if (be_setup.err)
+	return -1;
+
+      if (be_setup.be_count > 1)
+	{
+	  if (be_setup.be_class & ~(BE_MASK (BE_REGULAR) |
+				    BE_MASK (BE_MATRIX) |
+				    BE_MASK (BE_REDIRECT)))
+	    {
+	      conf_error_at_locus_range (NULL,
+			  "%s: %s",
+			  svc->locus_str,
+			  BITCOUNT (be_setup.be_class) == 1
+			    ? "multiple backends of this type are not allowed"
+			    : "service mixes backends of different types");
+	      return -1;
+	    }
+
+	  if (be_setup.be_class & BE_MASK (BE_REDIRECT))
+	    {
+	      conf_error_at_locus_range (NULL,
+			  "%s: warning: %s",
+			  svc->locus_str,
+			  (be_setup.be_class & (BE_MASK (BE_REGULAR) |
+						BE_MASK (BE_MATRIX)))
+			     ? "service mixes regular and redirect backends"
+			     : "service uses multiple redirect backends");
+	      conf_error_at_locus_range (NULL,
+			  "%s: %s",
+			  svc->locus_str,
+			  "see section \"DEPRECATED FEATURES\" in pound(8)");
+	    }
+	}
+
+      be_count += be_setup.be_count;
+    }
+
+  if (be_count == 0)
+    {
+      conf_error_at_locus_range (NULL, "%s: warning: no backends defined",
+				 svc->locus_str);
+    }
+
+  service_lb_init (svc);
+
+  return 0;
+}
 
 /*
  * Fix-up password file structures for use in restricted chroot
@@ -5127,7 +5151,7 @@ parse_config_file (char const *file, int nosyslog)
 
   if (cfgparser_open (file, NULL))
     return -1;
-  
+
   res = parser_loop (top_level_parsetab, &pound_defaults, &pound_defaults, NULL);
   if (res == 0)
     {
@@ -5141,7 +5165,7 @@ parse_config_file (char const *file, int nosyslog)
 #ifdef ENABLE_DYNAMIC_BACKENDS
       resolver_set_config (&pound_defaults.resolver);
 #endif
-      if (foreach_backend (backend_finalize,
+      if (foreach_service (service_finalize,
 			   &pound_defaults.named_backend_table))
 	return -1;
       if (worker_min_count > worker_max_count)
diff --git a/src/dynbe.c b/src/dynbe.c
index ac71b9c..ca6b175 100644
--- a/src/dynbe.c
+++ b/src/dynbe.c
@@ -28,7 +28,7 @@ dns_not_found_response_alloc (enum dns_resp_type type, BACKEND *be)
   if ((resp = calloc (1, sizeof (*resp))) != NULL)
     {
       struct timespec ts;
-      
+
       resp->type = type;
       resp->count = 0;
       get_negative_expire_time (&ts, be);
@@ -58,6 +58,15 @@ dns_addr_to_addrinfo (union dns_addr *da, struct addrinfo *ai)
     }
   ai->ai_addr = (struct sockaddr *) da;
 }
+
+static void
+response_override_ttl (struct dns_response *resp, unsigned ttl)
+{
+  struct timespec ts;
+
+  clock_gettime (CLOCK_REALTIME, &ts);
+  resp->expires = ts.tv_sec + ttl;
+}
 
 static void service_matrix_addr_update_backends (SERVICE *svc, BACKEND *mtx,
 						 struct dns_response *resp);
@@ -67,6 +76,13 @@ static void job_resolver (enum job_ctl ctl, void *arg,
 			  const struct timespec *ts);
 static void start_backend_removal (SERVICE *svc);
 
+static JOB_ID
+job_enqueue_resolver (struct timespec *ts, BACKEND *be)
+{
+  backend_ref (be);
+  return job_enqueue (ts, job_resolver, be);
+}
+
 static int
 backend_matrix_addr_update (BACKEND *be)
 {
@@ -85,13 +101,15 @@ backend_matrix_addr_update (BACKEND *be)
 	}
       /* fall through */
     case dns_success:
+      if (be->v.mtx.override_ttl)
+	response_override_ttl (resp, be->v.mtx.override_ttl);
       service_matrix_addr_update_backends (be->service, be, resp);
       dns_response_free (resp);
       break;
 
     case dns_temp_failure:
       get_negative_expire_time (&ts, be);
-      job_enqueue (&ts, job_resolver, be);
+      job_enqueue_resolver (&ts, be);
       break;
 
     case dns_failure:
@@ -119,13 +137,15 @@ backend_matrix_srv_update (BACKEND *be)
 	}
       /* fall through */
     case dns_success:
+      if (be->v.mtx.override_ttl)
+	response_override_ttl (resp, be->v.mtx.override_ttl);
       service_matrix_srv_update_backends (be->service, be, resp);
       dns_response_free (resp);
       break;
 
     case dns_temp_failure:
       get_negative_expire_time (&ts, be);
-      job_enqueue (&ts, job_resolver, be);
+      job_enqueue_resolver (&ts, be);
       break;
 
     case dns_failure:
@@ -164,7 +184,7 @@ backend_matrix_schedule_update (BACKEND *be)
     {
       struct timespec ts;
       clock_gettime (CLOCK_REALTIME, &ts);
-      job_enqueue (&ts, job_resolver, be);
+      job_enqueue_resolver (&ts, be);
     }
 }
 
@@ -174,18 +194,24 @@ job_resolver (enum job_ctl ctl, void *arg, const struct timespec *ts)
   BACKEND *be = arg;
   if (ctl == job_ctl_run)
     backend_matrix_update (be);
+  backend_unref (be);
 }
 
 static unsigned long
-djb_hash (const unsigned char *str, int length)
+djb_hash_add (unsigned long hash, const unsigned char *str, int length)
 {
-  unsigned long hash = 5381;
   int i;
   for (i = 0; i < length; i++, str++)
     hash = ((hash << 5) + hash) + (*str);
   return hash;
 }
 
+static unsigned long
+djb_hash (const unsigned char *str, int length)
+{
+  return djb_hash_add (5381, str, length);
+}
+
 static unsigned long
 BACKEND_hash (const BACKEND *b)
 {
@@ -197,7 +223,11 @@ BACKEND_hash (const BACKEND *b)
     }
   else /* if (b->be_type == BE_MATRIX) */
     {
-      return strhash_ci (b->v.mtx.hostname, strlen (b->v.mtx.hostname));
+      return djb_hash_add (strhash_ci (b->v.mtx.hostname,
+				       strlen (b->v.mtx.hostname)),
+			   (unsigned char*) &b->v.mtx.port,
+			   sizeof (b->v.mtx.port));
+
     }
 }
 
@@ -214,7 +244,11 @@ BACKEND_cmp (const BACKEND *a, const BACKEND *b)
       return memcmp (ap, bp, al);
     }
   else /* if (a->be_type == BE_MATRIX) */
-    return strcasecmp (a->v.mtx.hostname, b->v.mtx.hostname);
+    {
+      if (strcasecmp (a->v.mtx.hostname, b->v.mtx.hostname) == 0)
+	return a->v.mtx.port - b->v.mtx.port;
+      return 1;
+    }
 }
 
 #define HT_TYPE BACKEND
@@ -255,11 +289,12 @@ backend_table_addr_lookup (BACKEND_TABLE bt, union dns_addr *addr)
 }
 
 static BACKEND *
-backend_table_hostname_lookup (BACKEND_TABLE bt, char const *hostname)
+backend_table_hostname_lookup (BACKEND_TABLE bt, char const *hostname, int port)
 {
   BACKEND key;
   key.be_type = BE_MATRIX;
   key.v.mtx.hostname = (char*) hostname;
+  key.v.mtx.port = htons (port);
   return BACKEND_RETRIEVE (bt->hash, &key);
 }
 
@@ -298,7 +333,7 @@ backend_sweep (BACKEND *be, void *data)
   if (be->mark)
     {
       SERVICE *svc = be->service;
-      
+
       assert (be->refcount > 0);
       /* Mark backend as disabled so it won't get more requests. */
       be->disabled = 1;
@@ -307,7 +342,7 @@ backend_sweep (BACKEND *be, void *data)
 	case BE_REGULAR:
 	  {
 	    BALANCER *balancer;
-	    
+
 	    /* Remove any sessions associated with it. */
 	    service_session_remove_by_backend (svc, be);
 	    /* Remove it from the balancer. */
@@ -315,7 +350,7 @@ backend_sweep (BACKEND *be, void *data)
 	    balancer_remove_backend (balancer, be);
 	    if (DLIST_EMPTY (&balancer->backends))
 	      {
-		DLIST_REMOVE (&svc->backends, balancer, link);
+		DLIST_REMOVE (&svc->balancers, balancer, link);
 		free (balancer);
 	      }
 	  }
@@ -339,16 +374,16 @@ backend_sweep (BACKEND *be, void *data)
 
       /* If a load balancer stored this backend as current, reset it. */
       service_lb_reset (svc, be);
-      
+
       /*
        * Remove backend from the hash table and decrement its refcount.
        */
       backend_table_delete (tab, be);
       be->refcount--;
 
-      /* Add it to the list of removed backends. */
+      /* Add it to the list of backends to be removed. */
       DLIST_INSERT_TAIL (&svc->be_rem_head, be, link);
-      
+
       be->mark = 0;
     }
   pthread_mutex_unlock (&be->mut);
@@ -368,7 +403,8 @@ service_matrix_addr_update_backends (SERVICE *svc,
   pthread_mutex_lock (&svc->mut);
   pthread_mutex_lock (&mtx->mut);
 
-  balancer = balancer_list_get (&svc->backends, mtx->v.mtx.weight);
+  balancer = balancer_list_get (&svc->balancers, mtx->v.mtx.weight,
+				svc->balancer_algo);
   if (!mtx->disabled)
     {
       /* Mark all generated backends. */
@@ -404,35 +440,30 @@ service_matrix_addr_update_backends (SERVICE *svc,
 	      struct addrinfo ai;
 	      void *p;
 
-	      /* Generate new backend. */
-	      be = calloc (1, sizeof (*be));
-	      if (!be)
-		{
-		  lognomem ();
-		  break;
-		}
-
 	      dns_addr_to_addrinfo (&resp->addr[i], &ai);
 	      p = malloc (ai.ai_addrlen);
 	      if (!p)
 		{
 		  lognomem ();
-		  free (be);
 		  break;
 		}
 	      memcpy (p, ai.ai_addr, ai.ai_addrlen);
 	      ai.ai_addr = p;
+
+	      /* Generate new backend. */
+	      be = backend_create (BE_REGULAR, mtx->priority, &mtx->locus);
+	      if (!be)
+		{
+		  lognomem ();
+		  free (p);
+		  break;
+		}
 	      backend_matrix_to_regular (&mtx->v.mtx, &ai, &be->v.reg);
 	      be->service = mtx->service;
-	      be->locus = mtx->locus;
 	      be->locus_str = mtx->locus_str;
-	      be->be_type = BE_REGULAR;
-	      be->priority = mtx->priority;
 	      be->disabled = mtx->disabled;
-	      pthread_mutex_init (&be->mut, NULL);
-	      be->refcount = 1;
 	      be->v.reg.parent = mtx;
-	      
+
 	      /* Add it to the list of service backends and to the hash
 		 table. */
 	      balancer_add_backend (balancer, be);
@@ -446,38 +477,17 @@ service_matrix_addr_update_backends (SERVICE *svc,
 
       if (!DLIST_EMPTY (&svc->be_rem_head))
 	start_backend_removal (svc);
-      
+
       /* Reschedule next update. */
       ts.tv_sec = resp->expires;
       ts.tv_nsec = 0;
-      mtx->v.mtx.jid = job_enqueue (&ts, job_resolver, mtx);
+      mtx->v.mtx.jid = job_enqueue_resolver (&ts, mtx);
     }
 
   pthread_mutex_unlock (&mtx->mut);
   pthread_mutex_unlock (&svc->mut);
 }
 
-static int
-compute_priority (SERVICE *svc, struct dns_srv *srv, int total_weight)
-{
-  int result;
-
-  switch (svc->balancer_algo)
-    {
-    case BALANCER_ALGO_RANDOM:
-      result = srv->weight * 9 / total_weight;
-      break;
-
-    case BALANCER_ALGO_IWRR:
-      result = srv->weight;
-      break;
-
-    default:
-      abort ();
-    }
-  return result;
-}
-
 static void
 backend_set_prio (BACKEND *be, void *data)
 {
@@ -492,9 +502,11 @@ struct srv_stat
   int priority;
   int start;
   int count;
-  unsigned long total_weight;
+  int max_weight;
 };
 
+#define TOT_PRI_MAX ULONG_MAX
+
 static int
 analyze_srv_response (struct dns_response *resp, struct srv_stat **pstat)
 {
@@ -508,7 +520,7 @@ analyze_srv_response (struct dns_response *resp, struct srv_stat **pstat)
       *pstat = NULL;
       return 0;
     }
-  
+
   /* Count SRV groups. */
   for (i = 0; i < resp->count; i++)
     if (prio != resp->srv[i].priority)
@@ -533,22 +545,9 @@ analyze_srv_response (struct dns_response *resp, struct srv_stat **pstat)
 	  grp[j].priority = prio;
 	  grp[j].start = i;
 	}
-      if (TOT_PRI_MAX - grp[j].total_weight > resp->srv[i].weight)
-	{
-	  grp[j].total_weight += resp->srv[i].weight;
-	  grp[j].count++;
-	}
-      else
-	{
-	  logmsg (LOG_NOTICE,
-		  "SRV record %d %d %d %s overflows total priority",
-		  resp->srv[i].priority, resp->srv[i].weight,
-		  resp->srv[i].port, resp->srv[i].host);
-	  /* Skip rest of records with this priority. */
-	  for (; i + 1 < resp->count; i++)
-	    if (resp->srv[i].priority != prio)
-	      break;
-	}
+      if (grp[j].max_weight < resp->srv[i].weight)
+	grp[j].max_weight = resp->srv[i].weight;
+      grp[j].count++;
     }
 
   *pstat = grp;
@@ -572,11 +571,11 @@ service_matrix_srv_update_backends (SERVICE *svc, BACKEND *mtx,
 	{
 	  get_negative_expire_time (&ts, mtx);
 	}
-      else     
+      else
 	{
 	  int i;
 	  int mark = 1;
-	  
+
 	  /* Mark all generated backends. */
 	  backend_table_foreach (mtx->v.mtx.betab, backend_mark, &mark);
 
@@ -590,11 +589,15 @@ service_matrix_srv_update_backends (SERVICE *svc, BACKEND *mtx,
 		{
 		  struct dns_srv *srv = &resp->srv[stat[i].start + j];
 		  BACKEND *be = backend_table_hostname_lookup (mtx->v.mtx.betab,
-							       srv->host);
+							       srv->host,
+							       srv->port);
+		  int prio = mtx->v.mtx.ignore_srv_weight
+			       ? mtx->priority
+			       : (stat[i].max_weight == 0
+				  ? 1 : srv->weight);
+
 		  if (be)
 		    {
-		      int prio = compute_priority (svc, srv,
-						   stat[i].total_weight);
 		      if (be->priority != prio)
 			{
 			  /* Update priority of the matrix and all backends
@@ -612,22 +615,15 @@ service_matrix_srv_update_backends (SERVICE *svc, BACKEND *mtx,
 		       * Backend doesn't exist.  Create new matrix backend
 		       * using data from the SRV matrix and SRV RR.
 		       */
-		      be = calloc (1, sizeof (*be));
+		      be = backend_create (BE_MATRIX, prio, &mtx->locus);
 		      if (!be)
 			{
 			  lognomem ();
 			  break;
 			}
 		      be->service = mtx->service;
-		      be->locus = mtx->locus;
 		      be->locus_str = mtx->locus_str;
-		      be->be_type = BE_MATRIX;
-		      be->priority = compute_priority (svc, srv,
-						       stat->total_weight);
 		      be->disabled = 0;
-		      pthread_mutex_init (&be->mut, NULL);
-		      be->refcount = 1;
-		      
 		      be->v.mtx.hostname = strdup (srv->host);
 		      if (!be->v.mtx.hostname)
 			{
@@ -644,24 +640,26 @@ service_matrix_srv_update_backends (SERVICE *svc, BACKEND *mtx,
 		      be->v.mtx.ws_to = mtx->v.mtx.ws_to;
 		      be->v.mtx.ctx = mtx->v.mtx.ctx;
 		      be->v.mtx.servername = mtx->v.mtx.servername;
+		      be->v.mtx.override_ttl = mtx->v.mtx.override_ttl;
 		      be->v.mtx.betab = backend_table_new ();
 		      if (!be->v.mtx.betab)
 			{
 			  lognomem ();
+			  free (be->v.mtx.hostname);
 			  free (be);
 			  break;
 			}
 		      be->v.mtx.weight = srv->priority;
 		      be->v.mtx.parent = mtx;
-		      
+
 		      /*
 		       * Trigger regular backend creation.
 		       */
 		      backend_matrix_schedule_update (be);
-		      
+
 		      /* Add new matrix to the hash table. */
 		      backend_table_insert (mtx->v.mtx.betab, be);
-		      
+
 		      /*
 		       * Notice, that subsidiary matrix backends are not
 		       * added to the service backend list.  That would be
@@ -671,9 +669,9 @@ service_matrix_srv_update_backends (SERVICE *svc, BACKEND *mtx,
 		    }
 		}
 	    }
-	  
+
 	  free (stat);
-	  
+
 	  /* Remove all unreferenced backends. */
 	  backend_table_foreach (mtx->v.mtx.betab, backend_sweep,
 				 mtx->v.mtx.betab);
@@ -685,7 +683,7 @@ service_matrix_srv_update_backends (SERVICE *svc, BACKEND *mtx,
 	  ts.tv_sec = resp->expires;
 	  ts.tv_nsec = 0;
 	}
-      mtx->v.mtx.jid = job_enqueue (&ts, job_resolver, mtx);
+      mtx->v.mtx.jid = job_enqueue_resolver (&ts, mtx);
     }
   pthread_mutex_unlock (&mtx->mut);
   pthread_mutex_unlock (&svc->mut);
@@ -795,7 +793,7 @@ start_backend_removal (SERVICE *svc)
 void
 backend_ref (BACKEND *be)
 {
-  if (be && be->be_type == BE_REGULAR)
+  if (be)
     {
       pthread_mutex_lock (&be->mut);
       be->refcount++;
@@ -806,7 +804,7 @@ backend_ref (BACKEND *be)
 void
 backend_unref (BACKEND *be)
 {
-  if (be && be->be_type == BE_REGULAR)
+  if (be)
     {
       pthread_mutex_lock (&be->mut);
       assert (be->refcount > 0);
diff --git a/src/pound.h b/src/pound.h
index f30388f..180233c 100644
--- a/src/pound.h
+++ b/src/pound.h
@@ -488,13 +488,15 @@ struct be_matrix
   int family;           /* Address family for resolving hostname. */
   int resolve_mode;     /* Mode for resolving hostname. */
   unsigned retry_interval; /* Retry interval for failed queries. */
+  int ignore_srv_weight;   /* Ignore weight field in SRV RR. */
+  unsigned override_ttl;   /* Use this TTL instead of one returned from DNS. */
 
   unsigned to;		/* read/write time-out */
   unsigned conn_to;	/* connection time-out */
   unsigned ws_to;	/* websocket time-out */
   SSL_CTX *ctx;		/* CTX for SSL connections */
   char *servername;     /* SNI */
-  
+
   BACKEND_TABLE betab;  /* Table of regular backends generated from this
 			   matrix. */
   JOB_ID jid;           /* ID of the periodic job scheduled to update this
@@ -553,7 +555,7 @@ typedef struct _backend
   int mark;                     /* If set, this backend is a candidate for
 				   deletion. */
 
-  
+
   /* Statistics */
   pthread_mutex_t mut;		/* mutex for this back-end */
   unsigned long refcount;       /* reference counter */
@@ -590,6 +592,13 @@ static inline int backend_is_alive (BACKEND *be)
   return be->be_type != BE_REGULAR || be->v.reg.alive;
 }
 
+static inline int backend_is_active (BACKEND *be)
+{
+  return !be->disabled && backend_is_alive (be);
+}
+
+BACKEND *backend_create (BACKEND_TYPE type, int prio, struct locus_range *loc);
+
 typedef struct session
 {
   char *key;
@@ -752,8 +761,7 @@ typedef enum
     BALANCER_ALGO_IWRR,
   } BALANCER_ALGO;
 
-#define PRI_MAX_RANDOM 9
-#define PRI_MAX_IWRR   65535
+#define PRI_MAX   65535
 
 enum
   {
@@ -761,18 +769,30 @@ enum
     REWRITE_RESPONSE
   };
 
-#define TOT_PRI_MAX ULONG_MAX
+struct iwrr_balancer
+{
+  int round;
+  int max_pri;                  /* maximum priority */
+  BACKEND *cur;
+};
+
+struct rand_balancer
+{
+  unsigned long sum_pri;	/* sum of priorities of active backends */
+};
 
 typedef struct balancer
 {
-  int weight;
-  unsigned long tot_pri;	/* total priority of active backends */
-  int max_pri;                  /* maximum priority */
-  /* For IWRR balancer */
-  int iwrr_round;
-  BACKEND *iwrr_cur;
+  BALANCER_ALGO algo;
+  int weight;                   /* relative weight among other balancers */
+  unsigned act_num;             /* number of active backends */
   BACKEND_HEAD backends;
   DLIST_ENTRY (balancer) link;
+  union
+  {
+    struct iwrr_balancer iwrr;
+    struct rand_balancer rand;
+  };
 } BALANCER;
 
 typedef DLIST_HEAD (,balancer) BALANCER_LIST;
@@ -784,7 +804,7 @@ typedef struct _service
   char *locus_str;              /* Location in the config file, as string. */
   SERVICE_COND cond;
   REWRITE_RULE_HEAD rewrite[2];
-  BALANCER_LIST backends;
+  BALANCER_LIST balancers;
   BALANCER_ALGO balancer_algo;
   pthread_mutex_t mut;		/* mutex for this service */
   SESS_TYPE sess_type;
@@ -1072,20 +1092,20 @@ static inline void balancer_remove_backend (BALANCER *bl, BACKEND *be)
 }
 
 BALANCER *balancer_list_alloc (BALANCER_LIST *ml);
-BALANCER *balancer_list_get (BALANCER_LIST *ml, int n);
+BALANCER *balancer_list_get (BALANCER_LIST *ml, int n, BALANCER_ALGO algo);
 
 #define BALANCER_WEIGTH_MAX  65535
 
 static inline BALANCER *
 balancer_list_get_normal (BALANCER_LIST *ml)
 {
-  return balancer_list_get (ml, 0);
+  return balancer_list_get (ml, 0, BALANCER_ALGO_RANDOM);
 }
 
 static inline BALANCER *
 balancer_list_get_emerg (BALANCER_LIST *ml)
 {
-  return balancer_list_get (ml, BALANCER_WEIGTH_MAX);
+  return balancer_list_get (ml, BALANCER_WEIGTH_MAX, BALANCER_ALGO_RANDOM);
 }
 
 /*
@@ -1237,4 +1257,3 @@ int basic_auth (struct pass_file *pwf, struct http_request *req);
 
 void combinable_header_add (char const *name);
 int is_combinable_header (struct http_header *hdr);
-
diff --git a/src/svc.c b/src/svc.c
index a63b94d..61c0b23 100644
--- a/src/svc.c
+++ b/src/svc.c
@@ -328,8 +328,7 @@ expire_sessions (enum job_ctl ctl, void *data, const struct timespec *now)
     }
 
   if (!DLIST_EMPTY (&tab->head))
-    job_enqueue (&DLIST_FIRST (&tab->head)->expire,
-		 expire_sessions, svc);
+    job_enqueue (&DLIST_FIRST (&tab->head)->expire, expire_sessions, svc);
   pthread_mutex_unlock (&svc->mut);
 }
 
@@ -375,6 +374,7 @@ service_session_add (SERVICE *svc, const char *key, BACKEND *be)
   t->expire.tv_sec += svc->sess_ttl;
   if ((old = SESSION_INSERT (tab->hash, t)) != NULL)
     {
+      session_unlink (svc->sessions, old);
       session_free (old);
       logmsg (LOG_WARNING, "service_session_add() DUP");
     }
@@ -621,11 +621,27 @@ random_in_range (unsigned long max)
   return r % max;
 }
 
-/*
- * Pick a random back-end from a candidate list
- */
+static void
+rand_pri_init (BALANCER *bl)
+{
+  bl->rand.sum_pri = 0;
+}
+
+static int
+rand_pri_update (BALANCER *bl, BACKEND *be)
+{
+  if (LONG_MAX - bl->rand.sum_pri < be->priority)
+    {
+      logmsg (LOG_ERR, "%s: this backend overflows the sum of priorities",
+	      be->locus_str);
+      return 1;
+    }
+  bl->rand.sum_pri += be->priority;
+  return 0;
+}
+
 static BACKEND *
-rand_backend (BALANCER *balancer)
+rand_select (BALANCER *balancer)
 {
   BACKEND *be;
   int pri;
@@ -633,103 +649,154 @@ rand_backend (BALANCER *balancer)
   if (DLIST_EMPTY (&balancer->backends))
     return NULL;
   
-  pri = random_in_range (balancer->tot_pri);
+  pri = random_in_range (balancer->rand.sum_pri);
   DLIST_FOREACH (be, &balancer->backends, link)
     {
-      if (!backend_is_alive (be) || be->disabled)
-	continue;
-      if ((pri -= be->priority) < 0)
+      if (backend_is_active (be) && (pri -= be->priority) < 0)
 	break;
     }
   return be;
 }
 
-static inline void
+static void
 iwrr_init (BALANCER *balancer)
 {
-  balancer->iwrr_round = 0;
-  balancer->iwrr_cur = DLIST_FIRST (&balancer->backends);
+  balancer->iwrr.round = 0;
+  balancer->iwrr.cur = DLIST_FIRST (&balancer->backends);
+}
+
+static void
+iwrr_reset (BALANCER *balancer, BACKEND *be)
+{
+  if (balancer->iwrr.cur == be)
+    {
+      iwrr_init (balancer);
+    }
 }
 
 static BACKEND *
-iwrr_select (BALANCER *balancer)
+iwrr_select (BALANCER *bal)
 {
   BACKEND *be = NULL;
 
-  if (balancer->iwrr_cur == NULL)
+  if (bal->iwrr.cur == NULL)
     {
-      iwrr_init (balancer);
-      if (balancer->iwrr_cur == NULL)
+      iwrr_init (bal);
+      if (bal->iwrr.cur == NULL)
 	return NULL;
     }
   
   do
     {
-      if (!balancer->iwrr_cur->disabled &&
-	  backend_is_alive (balancer->iwrr_cur))
+      if (backend_is_active (bal->iwrr.cur))
 	{
-	  if (balancer->iwrr_round <= balancer->iwrr_cur->priority)
-	    be = balancer->iwrr_cur;
+	  if (bal->iwrr.round < bal->iwrr.cur->priority)
+	    be = bal->iwrr.cur;
 	}
-      if ((balancer->iwrr_cur = DLIST_NEXT (balancer->iwrr_cur, link)) == NULL)
+      if ((bal->iwrr.cur = DLIST_NEXT (bal->iwrr.cur, link)) == NULL)
 	{
-	  balancer->iwrr_cur = DLIST_FIRST (&balancer->backends);
-	  balancer->iwrr_round = (balancer->iwrr_round + 1) %
-	                            (balancer->max_pri + 1);
+	  bal->iwrr.cur = DLIST_FIRST (&bal->backends);
+	  bal->iwrr.round = (bal->iwrr.round + 1) % (bal->iwrr.max_pri + 1);
 	}
     }
   while (be == NULL);
   return be;
 }
 
-static BACKEND *
-balancer_select_backend (BALANCER_ALGO algo, BALANCER *balancer)
+static void
+iwrr_pri_init (BALANCER *bal)
+{
+  bal->iwrr.max_pri = 0;
+}
+
+static int
+iwrr_pri_update (BALANCER *bal, BACKEND *be)
+{
+  if (be->priority == INT_MAX)
+    {
+      logmsg (LOG_ERR, "%s: priority value too big", be->locus_str);
+      return 1;
+    }
+  if (bal->iwrr.max_pri < be->priority)
+    bal->iwrr.max_pri = be->priority;
+  return 0;
+}
+
+struct balancer_def
+{
+  void (*init) (BALANCER *);
+  void (*reset) (BALANCER *, BACKEND *be);
+  BACKEND *(*select) (BALANCER *);
+  void (*pri_init) (BALANCER *);
+  int (*pri_update) (BALANCER *, BACKEND *);
+};
+
+static struct balancer_def balancer_tab[] = {
+  [BALANCER_ALGO_RANDOM] = {
+    .select = rand_select,
+    .pri_init = rand_pri_init,
+    .pri_update = rand_pri_update
+  },
+  [BALANCER_ALGO_IWRR] = {
+    .init = iwrr_init,
+    .reset = iwrr_reset,
+    .select = iwrr_select,
+    .pri_init = iwrr_pri_init,
+    .pri_update = iwrr_pri_update
+  }
+};
+
+static inline BACKEND *
+balancer_select_backend (BALANCER *balancer)
 {
   BACKEND *be;
 
-  if (DLIST_EMPTY (&balancer->backends))
-    return NULL;
   if (DLIST_NEXT (DLIST_FIRST (&balancer->backends), link) == NULL)
     {
       be = DLIST_FIRST (&balancer->backends);
-      if (!be->disabled && backend_is_alive (be))
+      if (backend_is_active (be))
 	return be;
       return NULL;
     }
+  return balancer_tab[balancer->algo].select (balancer);
+}
 
-  switch (algo)
-    {
-    case BALANCER_ALGO_RANDOM:
-      be = rand_backend (balancer);
-      break;
+static inline void
+balancer_init (BALANCER *balancer)
+{
+  if (balancer_tab[balancer->algo].init)
+    balancer_tab[balancer->algo].init (balancer);
+}
 
-    case BALANCER_ALGO_IWRR:
-      be = iwrr_select (balancer);
-    }
-  return be;
+static inline void
+balancer_reset (BALANCER *balancer, BACKEND *be)
+{
+  if (balancer_tab[balancer->algo].reset)
+    balancer_tab[balancer->algo].reset (balancer, be);
 }
 
-void
-balancer_init (BALANCER_ALGO algo, BALANCER *balancer)
+static inline void
+balancer_pri_init (BALANCER *balancer)
 {
-  switch (algo)
-    {
-    case BALANCER_ALGO_RANDOM:
-      break;
+  if (balancer_tab[balancer->algo].pri_init)
+    balancer_tab[balancer->algo].pri_init (balancer);
+}
 
-    case BALANCER_ALGO_IWRR:
-      iwrr_init (balancer);
-      break;
-    }
+static inline int
+balancer_pri_update (BALANCER *balancer, BACKEND *be)
+{
+  if (balancer_tab[balancer->algo].pri_update)
+    return balancer_tab[balancer->algo].pri_update (balancer, be);
+  return 0;
 }
 
 BACKEND *
 service_lb_select_backend (SERVICE *svc)
 {
   BALANCER *balancer;
-  DLIST_FOREACH (balancer, &svc->backends, link)
+  DLIST_FOREACH (balancer, &svc->balancers, link)
     {
-      BACKEND *be = balancer_select_backend (svc->balancer_algo, balancer);
+      BACKEND *be = balancer_select_backend (balancer);
       if (be)
 	return be;
     }
@@ -740,26 +807,19 @@ void
 service_lb_init (SERVICE *svc)
 {
   BALANCER *balancer;
-  DLIST_FOREACH (balancer, &svc->backends, link)
+  DLIST_FOREACH (balancer, &svc->balancers, link)
     {
-      balancer_init (svc->balancer_algo, balancer);
+      balancer_init (balancer);
     }
 }
 
 void
 service_lb_reset (SERVICE *svc, BACKEND *be)
 {
-  if (svc->balancer_algo == BALANCER_ALGO_IWRR)
+  BALANCER *balancer;
+  DLIST_FOREACH (balancer, &svc->balancers, link)
     {
-      BALANCER *balancer;
-      DLIST_FOREACH (balancer, &svc->backends, link)
-	{
-	  if (balancer->iwrr_cur == be)
-	    {
-	      balancer_init (svc->balancer_algo, balancer);
-	      break;
-	    }
-	}
+      balancer_reset (balancer, be);
     }
 }
 
@@ -782,8 +842,8 @@ find_backend_by_key (SERVICE *svc, char const *key)
   if ((res = service_session_find (svc, keybuf)) == NULL)
     {
       /* no session yet - create one */
-      res = service_lb_select_backend (svc);
-      service_session_add (svc, keybuf, res);
+      if ((res = service_lb_select_backend (svc)) != NULL)
+	service_session_add (svc, keybuf, res);
     }
 
   return res;
@@ -907,9 +967,9 @@ static int
 service_has_backends (SERVICE *svc)
 {
   BALANCER *bl;
-  DLIST_FOREACH (bl, &svc->backends, link)
+  DLIST_FOREACH (bl, &svc->balancers, link)
     {
-      if (!DLIST_EMPTY (&bl->backends))
+      if (bl->act_num > 0)
 	return 1;
     }
   return 0;
@@ -1033,22 +1093,23 @@ upd_session (SERVICE *svc, HTTP_HEADER_LIST *headers, BACKEND *be)
  */
 void
 balancer_recompute_pri_unlocked (BALANCER *bl,
-				     void (*cb) (BACKEND *, void *),
-				     void *data)
+				 void (*cb) (BACKEND *, void *),
+				 void *data)
 {
   BACKEND *b;
   
-  bl->tot_pri = 0;
-  bl->max_pri = 0;
+  bl->act_num = 0;
+  balancer_pri_init (bl);
   DLIST_FOREACH (b, &bl->backends, link)
     {
       if (cb)
 	cb (b, data);
-      if (backend_is_alive (b) && !b->disabled)
+      if (backend_is_active (b))
 	{
-	  bl->tot_pri += b->priority;
-	  if (bl->max_pri < b->priority)
-	    bl->max_pri = b->priority;
+	  if (balancer_pri_update (bl, b))
+	    b->disabled = 1;
+	  else
+	    bl->act_num++;
 	}
     }
 }
@@ -1059,7 +1120,7 @@ service_recompute_pri_unlocked (SERVICE *svc,
 				void *data)
 {
   BALANCER *bl;
-  DLIST_FOREACH (bl, &svc->backends, link)
+  DLIST_FOREACH (bl, &svc->balancers, link)
     {
       balancer_recompute_pri_unlocked (bl, cb, data);
     }
@@ -1085,7 +1146,7 @@ service_recompute_pri (SERVICE *svc, BALANCER *bl,
     balancer_recompute_pri_unlocked (bl, cb, data);
   else
     {
-      DLIST_FOREACH (bl, &svc->backends, link)
+      DLIST_FOREACH (bl, &svc->balancers, link)
 	{
 	  balancer_recompute_pri_unlocked (bl, cb, data);
 	}
@@ -1599,9 +1660,8 @@ touch_be (enum job_ctl ctl, void *data, const struct timespec *ts)
 	      if (!be->disabled)
 		{
 		  pthread_mutex_lock (&be->service->mut);
-		  be->balancer->tot_pri += be->priority;
-		  if (be->balancer->max_pri < be->priority)
-		    be->balancer->max_pri = be->priority;
+		  balancer_pri_update (be->balancer, be);
+		  be->balancer->act_num++;
 		  pthread_mutex_unlock (&be->service->mut);
 		}
 	    }
@@ -1805,7 +1865,7 @@ session_backend_index (SESSION *sess)
   int n = 0;
   BALANCER *balancer;
 
-  DLIST_FOREACH (balancer, &sess->backend->service->backends, link)
+  DLIST_FOREACH (balancer, &sess->backend->service->balancers, link)
     {
       BACKEND *be;
       DLIST_FOREACH (be, &balancer->backends, link)
@@ -1981,7 +2041,7 @@ find_backend_index (BACKEND *be)
 {
   int i = 0;
   BALANCER *balancer;
-  DLIST_FOREACH (balancer, &be->service->backends, link)
+  DLIST_FOREACH (balancer, &be->service->balancers, link)
     {
       BACKEND *b;
       DLIST_FOREACH (b, &balancer->backends, link)
@@ -2082,8 +2142,8 @@ backend_serialize (BACKEND *be)
 		      || json_object_set (obj, "family",
 					  json_new_integer (be->v.mtx.family))
 		      || json_object_set (obj, "servername",
-					  be->v.reg.servername
-					  ? json_new_string (be->v.reg.servername)
+					  be->v.mtx.servername
+					  ? json_new_string (be->v.mtx.servername)
 					  : json_new_null ())
 		      || backend_serialize_dyninfo (obj, be);
 		    break;
@@ -2182,7 +2242,7 @@ service_serialize (SERVICE *svc)
 	  || json_object_set (obj, "enabled", json_new_bool (!svc->disabled))
 	  || json_object_set (obj, "session_type", json_new_string (typename ? typename : "UNKNOWN"))
 	  || json_object_set (obj, "sessions", service_session_serialize (svc))
-	  || json_object_set (obj, "backends", backends_serialize (&svc->backends)))
+	  || json_object_set (obj, "backends", backends_serialize (&svc->balancers)))
 	{
 	  json_value_free (obj);
 	  obj = NULL;
@@ -2534,7 +2594,7 @@ locate_backend (SERVICE *svc, IDENT id)
     {
       long n = 0;
       BALANCER *balancer;
-      DLIST_FOREACH (balancer, &svc->backends, link)
+      DLIST_FOREACH (balancer, &svc->balancers, link)
 	{
 	  BACKEND *be;
 	  DLIST_FOREACH (be, &balancer->backends, link)
@@ -2680,7 +2740,7 @@ static int
 service_has_control (SERVICE *svc)
 {
   BALANCER *balancer;
-  DLIST_FOREACH (balancer, &svc->backends, link)
+  DLIST_FOREACH (balancer, &svc->balancers, link)
     {
       BACKEND *be;
       DLIST_FOREACH (be, &balancer->backends, link)
@@ -3041,7 +3101,7 @@ itr_service_backends (SERVICE *svc, void *data)
 {
   struct service_backends_iterator *itp = data;
   BALANCER *balancer;
-  DLIST_FOREACH (balancer, &svc->backends, link)
+  DLIST_FOREACH (balancer, &svc->balancers, link)
     {
       BACKEND *be;
       DLIST_FOREACH (be, &balancer->backends, link)
@@ -3065,9 +3125,9 @@ foreach_backend (BACKEND_ITERATOR itr, void *data)
 }
 
 BALANCER *
-balancer_list_get (BALANCER_LIST *ml, int weight)
+balancer_list_get (BALANCER_LIST *ml, int weight, BALANCER_ALGO algo)
 {
-  BALANCER *bl, *new_list;
+  BALANCER *bl, *new_bl;
   DLIST_FOREACH (bl, ml, link)
     {
       if (weight == bl->weight)
@@ -3076,17 +3136,18 @@ balancer_list_get (BALANCER_LIST *ml, int weight)
 	break;
     }
 
-  if ((new_list = calloc (1, sizeof (*bl))) == NULL)
+  if ((new_bl = calloc (1, sizeof (*new_bl))) == NULL)
     return NULL;
-  new_list->weight = weight;
-  DLIST_INIT (&new_list->backends);
+  new_bl->weight = weight;
+  new_bl->algo = algo;
+  DLIST_INIT (&new_bl->backends);
   
   if (bl)
-    DLIST_INSERT_BEFORE (ml, bl, new_list, link);
+    DLIST_INSERT_BEFORE (ml, bl, new_bl, link);
   else
-    DLIST_INSERT_TAIL (ml, new_list, link);
+    DLIST_INSERT_TAIL (ml, new_bl, link);
 
-  return new_list;
+  return new_bl;
 }
 
 void
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 632822b..37ccb53 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -74,7 +74,10 @@ TESTSUITE_AT = \
  config.at\
  disable.at\
  dyn_a.at\
+ dyn_a_ttl.at\
  dyn_srv.at\
+ dyn_srv_0.at\
+ dyn_srv_ttl.at\
  echo.at\
  err503.at\
  errfile.at\
@@ -140,7 +143,10 @@ AM_CPPFLAGS = -I$(top_srcdir)/src
 tmplrun_LDADD = ../src/libpound.a
 
 if COND_DYNAMIC_BACKENDS
+if COND_BUILD_FAKEDNS
 noinst_LTLIBRARIES = libfakedns.la
 libfakedns_la_SOURCES = fakedns.c
 libfakedns_la_LDFLAGS = -module -export-dynamic -avoid-version -rpath '/lib'
+noinst_PROGRAMS += getsoa
+endif
 endif
diff --git a/tests/dyn_a_ttl.at b/tests/dyn_a_ttl.at
new file mode 100644
index 0000000..a6c073b
--- /dev/null
+++ b/tests/dyn_a_ttl.at
@@ -0,0 +1,90 @@
+# This file is part of pound testsuite. -*- autotest -*-
+# Copyright (C) 2024 Sergey Poznyakoff
+#
+# Pound is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pound 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 General Public License
+# along with pound.  If not, see <http://www.gnu.org/licenses/>.
+AT_SETUP([A record (TTL override)])
+AT_KEYWORDS([dynamic a_rr a_rr_ttl])
+AT_CHECK([PT_PREREQ_DYNAMIC_BACKENDS
+PT_PREREQ_FAKEDNS])
+
+m4_pushdef([HARNESS_OPTIONS],[--fakedns=$LIBFAKEDNS])
+PT_CHECK([ListenHTTP
+	Service
+		Balancer iwrr
+		Backend
+			Resolve all
+			Address "be.pound.example.org"
+			Family inet
+			OverrideTTL 5
+		End
+	End
+End
+],
+[[zonefile
+$ORIGIN example.org.
+@   IN SOA  mname rname 1 2h 1h 2w 1h
+$ORIGIN pound.example.org.
+
+be	300	A       127.0.0.1
+end
+
+sleep 3
+
+backends 1 0
+[{
+   "type":"matrix",
+   "family":2,
+   "resolve_mode":"all",
+   "hostname":"be.pound.example.org",
+   "weight":0
+},
+{
+   "type":"backend",
+   "parent":0,
+   "weight":0,
+   "priority":5,
+   "address":"127.0.0.1:80"
+}]
+end
+
+zonefile
+$ORIGIN example.org.
+@   IN SOA  mname rname 1 2h 1h 2w 1h
+$ORIGIN pound.example.org.
+
+be	300	A       127.0.0.2
+end
+
+sleep 3
+
+backends 1 0
+[{
+   "type":"matrix",
+   "family":2,
+   "resolve_mode":"all",
+   "hostname":"be.pound.example.org",
+   "weight":0
+},
+{
+   "type":"backend",
+   "parent":0,
+   "weight":0,
+   "priority":5,
+   "address":"127.0.0.2:80"
+}]
+end
+
+]])
+m4_popdef([HARNESS_OPTIONS])
+AT_CLEANUP
diff --git a/tests/dyn_srv_0.at b/tests/dyn_srv_0.at
new file mode 100644
index 0000000..cbb3b5b
--- /dev/null
+++ b/tests/dyn_srv_0.at
@@ -0,0 +1,155 @@
+# This file is part of pound testsuite. -*- autotest -*-
+# Copyright (C) 2024 Sergey Poznyakoff
+#
+# Pound is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pound 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 General Public License
+# along with pound.  If not, see <http://www.gnu.org/licenses/>.
+AT_SETUP([SRV record (0 weights)])
+AT_KEYWORDS([dynamic srv_rr srv_rr_0])
+AT_CHECK([PT_PREREQ_DYNAMIC_BACKENDS
+PT_PREREQ_FAKEDNS])
+
+m4_pushdef([HARNESS_OPTIONS],[--fakedns=$LIBFAKEDNS])
+PT_CHECK([ListenHTTP
+	Service
+		Balancer iwrr
+		Backend
+			Resolve srv
+			Address "_proxy._tcp.pound.example.org"
+			Family inet
+		End
+	End
+End
+],
+[[mkbackend 127.0.0.1
+mkbackend 127.0.0.2
+mkbackend 127.0.0.3
+
+zonefile
+$ORIGIN example.org.
+@   IN SOA  mname rname 1 2h 1h 2w 1h
+$ORIGIN pound.example.org.
+
+be0             60      A       127.0.0.1
+be1             60      A       127.0.0.2
+be2             60      A       127.0.0.3
+
+$ORIGIN _tcp.pound.example.org.
+; _srv  TTL     SRV     prio    weight  port target.
+_proxy  10      SRV     10      0       ${BACKEND0:PORT} be0.pound.example.org.
+_proxy  10      SRV     10      0       ${BACKEND1:PORT} be1.pound.example.org.
+_proxy  10      SRV     10      0       ${BACKEND2:PORT} be2.pound.example.org.
+end
+
+sleep 3
+
+backends 1 0
+[{
+   "type":"matrix",
+   "family":2,
+   "resolve_mode":"srv",
+   "hostname":"_proxy._tcp.pound.example.org",
+   "weight":0
+},
+{
+   "type":"backend",
+   "parent":0,
+   "weight":10,
+   "priority":1,
+   "address":"${BACKEND0}"
+},
+{
+   "type":"backend",
+   "parent":0,
+   "weight":10,
+   "priority":1,
+   "address":"${BACKEND1}"
+},
+{
+   "type":"backend",
+   "parent":0,
+   "weight":10,
+   "priority":1,
+   "address":"${BACKEND2}"
+}]
+end
+]])
+
+PT_CHECK([ListenHTTP
+	Service
+		Balancer iwrr
+		Backend
+			Resolve srv
+			Address "_proxy._tcp.pound.example.org"
+			Family inet
+			IgnoreSRVWeight true
+		End
+	End
+End
+],
+[[mkbackend 127.0.0.1
+mkbackend 127.0.0.2
+mkbackend 127.0.0.3
+
+zonefile
+$ORIGIN example.org.
+@   IN SOA  mname rname 1 2h 1h 2w 1h
+$ORIGIN pound.example.org.
+
+be0             60      A       127.0.0.1
+be1             60      A       127.0.0.2
+be2             60      A       127.0.0.3
+
+$ORIGIN _tcp.pound.example.org.
+; _srv  TTL     SRV     prio    weight  port target.
+_proxy  10      SRV     10      0       ${BACKEND0:PORT} be0.pound.example.org.
+_proxy  10      SRV     10      0       ${BACKEND1:PORT} be1.pound.example.org.
+_proxy  10      SRV     10      0       ${BACKEND2:PORT} be2.pound.example.org.
+end
+
+sleep 3
+
+backends 1 0
+[{
+   "type":"matrix",
+   "family":2,
+   "resolve_mode":"srv",
+   "hostname":"_proxy._tcp.pound.example.org",
+   "weight":0
+},
+{
+   "type":"backend",
+   "parent":0,
+   "weight":10,
+   "priority":5,
+   "address":"${BACKEND0}"
+},
+{
+   "type":"backend",
+   "parent":0,
+   "weight":10,
+   "priority":5,
+   "address":"${BACKEND1}"
+},
+{
+   "type":"backend",
+   "parent":0,
+   "weight":10,
+   "priority":5,
+   "address":"${BACKEND2}"
+}]
+end
+]])
+
+m4_popdef([HARNESS_OPTIONS])
+AT_CLEANUP
+
diff --git a/tests/dyn_srv_ttl.at b/tests/dyn_srv_ttl.at
new file mode 100644
index 0000000..56d9158
--- /dev/null
+++ b/tests/dyn_srv_ttl.at
@@ -0,0 +1,188 @@
+# This file is part of pound testsuite. -*- autotest -*-
+# Copyright (C) 2024 Sergey Poznyakoff
+#
+# Pound is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pound 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 General Public License
+# along with pound.  If not, see <http://www.gnu.org/licenses/>.
+AT_SETUP([SRV record (TTL override)])
+AT_KEYWORDS([dynamic srv_rr_ttl])
+
+AT_CHECK([PT_PREREQ_DYNAMIC_BACKENDS
+PT_PREREQ_FAKEDNS])
+
+m4_pushdef([HARNESS_OPTIONS],[--fakedns=$LIBFAKEDNS])
+PT_CHECK([ListenHTTP
+	Service
+		Balancer iwrr
+		Backend
+			Resolve srv
+			Address "_proxy._tcp.pound.example.org"
+			Family inet
+			OverrideTTL 5
+		End
+	End
+End
+],
+[[mkbackend 127.0.0.1
+mkbackend 127.0.0.2
+mkbackend 127.0.0.3
+mkbackend 127.0.0.4
+
+zonefile
+$ORIGIN example.org.
+@   IN SOA  mname rname 1 2h 1h 2w 1h
+$ORIGIN pound.example.org.
+
+be0             60      A       127.0.0.1
+be1             60      A       127.0.0.2
+be2            120      A       127.0.0.3
+
+$ORIGIN _tcp.pound.example.org.
+; _srv  TTL     SRV     prio    weight  port target.
+_proxy  300     SRV     10      40      ${BACKEND0:PORT} be0.pound.example.org.
+_proxy  300     SRV     10      70      ${BACKEND1:PORT} be1.pound.example.org.
+_proxy  300     SRV     20      10      ${BACKEND2:PORT} be2.pound.example.org.
+end
+
+sleep 3
+backends 1 0
+[{
+   "type":"matrix",
+   "family":2,
+   "resolve_mode":"srv",
+   "hostname":"_proxy._tcp.pound.example.org",
+   "weight":0
+},
+{
+   "type":"backend",
+   "parent":0,
+   "weight":10,
+   "priority":40,
+   "address":"${BACKEND0}"
+},
+{
+   "type":"backend",
+   "parent":0,
+   "weight":10,
+   "priority":70,
+   "address":"${BACKEND1}"
+},
+{
+   "type":"backend",
+   "parent":0,
+   "weight":20,
+   "priority":10,
+   "address":"${BACKEND2}"
+}]   
+end
+
+zonefile
+$ORIGIN example.org.
+@   IN SOA  mname rname 1 2h 1h 2w 1h
+$ORIGIN pound.example.org.
+
+be0             60      A       127.0.0.1
+be1             60      A       127.0.0.2
+be2            120      A       127.0.0.3
+
+$ORIGIN _tcp.pound.example.org.
+; _srv  TTL     SRV     prio    weight  port target.
+_proxy  300     SRV     10      70      ${BACKEND0:PORT} be0.pound.example.org.
+_proxy  300     SRV     10      10      ${BACKEND1:PORT} be1.pound.example.org.
+_proxy  300     SRV     20      10      ${BACKEND2:PORT} be2.pound.example.org.
+end
+
+sleep 5
+backends 1 0
+[{
+   "type":"matrix",
+   "family":2,
+   "resolve_mode":"srv",
+   "hostname":"_proxy._tcp.pound.example.org",
+   "weight":0
+},
+{
+   "type":"backend",
+   "parent":0,
+   "weight":10,
+   "priority":10,
+   "address":"${BACKEND1}"
+},
+{
+   "type":"backend",
+   "parent":0,
+   "weight":10,
+   "priority":70,
+   "address":"${BACKEND0}"
+},
+{
+   "type":"backend",
+   "parent":0,
+   "weight":20,
+   "priority":10,
+   "address":"${BACKEND2}"
+}]   
+end
+
+zonefile
+$ORIGIN example.org.
+@   IN SOA  mname rname 1 2h 1h 2w 1h
+$ORIGIN pound.example.org.
+
+be0             60      A       127.0.0.1
+be1             60      A       127.0.0.4
+be2            120      A       127.0.0.3
+
+$ORIGIN _tcp.pound.example.org.
+; _srv  TTL     SRV     prio    weight  port target.
+_proxy  300     SRV     10      70      ${BACKEND0:PORT} be0.pound.example.org.
+_proxy  300     SRV     10      10      ${BACKEND3:PORT} be1.pound.example.org.
+_proxy  300     SRV     20      10      ${BACKEND2:PORT} be2.pound.example.org.
+end
+
+sleep 5
+
+backends 1 0
+[{
+   "type":"matrix",
+   "family":2,
+   "resolve_mode":"srv",
+   "hostname":"_proxy._tcp.pound.example.org",
+   "weight":0
+},
+{
+   "type":"backend",
+   "parent":0,
+   "weight":10,
+   "priority":10,
+   "address":"${BACKEND3}"
+},
+{
+   "type":"backend",
+   "parent":0,
+   "weight":10,
+   "priority":70,
+   "address":"${BACKEND0}"
+},
+{
+   "type":"backend",
+   "parent":0,
+   "weight":20,
+   "priority":10,
+   "address":"${BACKEND2}"
+}]   
+end
+
+]])
+
+m4_popdef([HARNESS_OPTIONS])
+AT_CLEANUP
diff --git a/tests/getsoa.c b/tests/getsoa.c
new file mode 100644
index 0000000..fc348a5
--- /dev/null
+++ b/tests/getsoa.c
@@ -0,0 +1,139 @@
+/*
+ * NAME
+ *   getsoa - retrieve soa record of an NS zone.
+ *
+ * SYNOPSIS
+ *   LD_PRELOAD=/path/to/libfakedns.so getsoa [-d] ZONE NS
+ *
+ * DESCRIPTION
+ *   Queries NS for the SOA record of an NS zone and displays it as
+ *   a series of space-delimited values.  Used as a helper for the
+ *   self-test mode in poundharness.pl.
+ *
+ * OPTIONS
+ *   -d     Enables adns debugging output.
+ *
+ * SEE ALSO
+ *   fakedns.c
+ *
+ * LICENSE
+ *   This library is part of pound testsuite.
+ *   Copyright (C) 2024 Sergey Poznyakoff
+ *
+ *   Pound is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   Pound 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 General Public License
+ *   along with pound.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <adns.h>
+#include <assert.h>
+#include <string.h>
+
+char *progname;
+
+static void
+usage (FILE *fp)
+{
+  fprintf (stderr, "%s ZONE NS\n", progname);
+}
+
+#define NSSTR "nameserver "
+
+#define DEFAULT_QFLAGS	       \
+	(adns_qf_cname_loose | \
+	 adns_qf_quoteok_query | \
+	 adns_qf_quoteok_cname | \
+	 adns_qf_quoteok_anshost)
+
+/*
+  getsoa ZONE NS
+ */
+int
+main (int argc, char **argv)
+{
+  char *zone;
+  char *nameserver = NULL;
+  adns_state state;
+  adns_initflags flags = adns_if_none;
+  int rc, i;
+  char *config_text = NULL;
+  adns_answer *ans = NULL;
+
+  progname = argv[0];
+
+  while ((rc = getopt (argc, argv, "dh")) != EOF)
+    {
+      switch (rc)
+	{
+	case 'd':
+	  flags |= adns_if_debug;
+	  break;
+
+	case 'h':
+	  usage (stdout);
+	  return 0;
+
+	default:
+	  return 1;
+	}
+    }
+
+  argc -= optind;
+  argv += optind;
+
+  switch (argc)
+    {
+    case 2:
+      nameserver = argv[1];
+      zone = argv[0];
+      break;
+    default:
+      fprintf (stderr, "%s: bad number of arguments\n", progname);
+      usage (stderr);
+      return 1;
+    }
+
+  config_text = malloc (sizeof (NSSTR) + 1 + strlen (nameserver));
+  assert (config_text != NULL);
+  strcat (strcpy (config_text, NSSTR), nameserver);
+
+  rc = adns_init_strcfg (&state, flags, stderr, config_text);
+  if (rc)
+    {
+      fprintf (stderr, "%s: can't initialize DNS state: %s",
+	       progname, strerror (rc));
+      return 1;
+    }
+
+  rc = adns_synchronous (state, zone, adns_r_soa, DEFAULT_QFLAGS, &ans);
+  if (rc != adns_s_ok)
+    {
+      fprintf (stderr, "%s: %s\n", progname, adns_strerror (rc));
+      return 1;
+    }
+
+  for (i = 0; i < ans->nrrs; i++)
+    {
+      printf ("%s. %s. %ld %ld %ld %ld %ld\n",
+	      ans->rrs.soa[i].mname,
+	      ans->rrs.soa[i].rname,
+	      ans->rrs.soa[i].serial,
+	      ans->rrs.soa[i].refresh,
+	      ans->rrs.soa[i].retry,
+	      ans->rrs.soa[i].expire,
+	      ans->rrs.soa[i].minimum);
+    }
+  free (ans);
+  return 0;
+}
diff --git a/tests/perllib/PoundNS.pm b/tests/perllib/PoundNS.pm
index 132e3e6..a8b4c35 100644
--- a/tests/perllib/PoundNS.pm
+++ b/tests/perllib/PoundNS.pm
@@ -24,7 +24,7 @@ use IO::Socket::IP;
 use File::stat;
 
 # This module modifies internals of Net::DNS::Nameserver, therefore it
-# is not guaranteed that it will work with any other version than that
+# is not guaranteed that it will work with any version other than that
 # for which it has been written.
 croak 'unsupported version of Net::DNS::Nameserver'
     unless $Net::DNS::Nameserver::VERSION == 1990;
diff --git a/tests/poundharness.pl b/tests/poundharness.pl
index b967253..5bf6470 100644
--- a/tests/poundharness.pl
+++ b/tests/poundharness.pl
@@ -68,6 +68,12 @@ if ($@) {
     exit(EX_SKIP);
 }
 
+eval "require JSON";
+if ($@) {
+    print STDERR "required module JSON not present\n";
+    exit(::EX_SKIP);
+}
+
 sub cleanup {
     if ($pound_pid) {
 	if ($verbose) {
@@ -81,6 +87,7 @@ sub cleanup {
 ## -----------------------------------
 
 my %status_codes;
+share(%status_codes);
 
 $SIG{QUIT} = $SIG{HUP} = $SIG{TERM} = $SIG{INT} = \&cleanup;
 
@@ -108,7 +115,7 @@ sub sigchild {
     if ($pid == -1) {
 	return
     }
-    if ($pid != $pound_pid) {
+    if (!defined($pound_pid) || $pid != $pound_pid) {
 	$status_codes{$pid} = $?;
 	$SIG{CHLD} = \&sigchild;
 	return;
@@ -122,6 +129,30 @@ sub sigchild {
 };
 $SIG{CHLD} = \&sigchild;
 
+sub fakedns_self_check {
+    my $params = 'ns1.example.org. rname@example.org. 42 7200 3600 1209600 3600';
+
+    $nameserver->ZoneUpdate(
+	'$ORIGIN example.org.',
+	'@ IN SOA '.$params
+    );
+
+    local $ENV{LD_PRELOAD} = $fakedns;
+    if (open(my $fh, '-|', 'getsoa', 'example.org', '192.0.2.24')) {
+	chomp(my $s = <$fh>);
+	if ($s ne $params) {
+	    print STDERR "Self-check: got $s\n";
+	    return;
+	}
+	if (<$fh>) {
+	    print STDERR "Self-check: garbage after response: $_\n";
+	    return;
+	}
+	close $fh;
+	return 1;
+    }
+}
+
 ## Start the program
 ## -----------------
 sub usage_error {
@@ -161,6 +192,11 @@ if ($fakedns) {
 
     $nameserver = PoundNS->new or die "can't create nameserver";
     ($ENV{FAKEDNS_UDP_PORT}, $ENV{FAKEDNS_TCP_PORT}) = $nameserver->start_server();
+
+    unless (fakedns_self_check()) {
+	print STDERR "Self-check failed\n";
+	exit(EX_SKIP);
+    }
 }
 
 foreach my $file (@preproc_files) {
@@ -274,7 +310,7 @@ sub preproc {
 	print $out <<EOT;
 # Initial settings by $0
 Daemon 0
-Control "pound.ctl"	    
+Control "pound.ctl"
 LogFacility -
 EOT
     ;
@@ -476,6 +512,30 @@ sub failures { shift->{failures} }
 
 sub http { shift->{http} }
 
+sub transcript {
+    my ($self, $text, %opt) = @_;
+    if (my $fh = $self->{xscript}) {
+	if (my $locus = delete $opt{locus}) {
+	    print $fh "$self->{filename}:$locus->{BEG}-$locus->{END}";
+	} else {
+	    print $fh "$self->{filename}:$self->{line}";
+	}
+	print $fh ": ";
+	print $fh $text;
+	print $fh "\n";
+    }
+}
+
+sub transcript_ml {
+    my $self = shift;
+    if (my $fh = $self->{xscript}) {
+	print $fh join("\n", @_)."\n";
+    }
+}
+
+sub transcript_ok { shift->transcript("OK", @_) }
+sub transcript_fail { shift->transcript("FAIL", @_) }
+
 sub send_and_expect {
     my ($self, $lst, $host, $stats) = @_;
     my $url = $lst->proto . '://' .
@@ -502,14 +562,12 @@ sub send_and_expect {
     if ($verbose) {
 	print "URL $self->{REQ}{METHOD} $url\n";
     }
-    if (my $fh = $self->{xscript}) {
-	print $fh "$self->{filename}:$self->{REQ}{BEG}-$self->{REQ}{END}: sending $self->{REQ}{METHOD} $url to server $self->{server}\n";
-    }
+
+    $self->transcript("sending $self->{REQ}{METHOD} $url to server $self->{server}",
+		      locus => $self->{REQ});
+
     my $response = $self->http->request($self->{REQ}{METHOD}, $url, \%options);
-    if (my $fh = $self->{xscript}) {
-	print $fh "$self->{filename}:$self->{REQ}{BEG}-$self->{REQ}{END}: got:\n";
-	print $fh Dumper([$response]);
-    }
+    $self->transcript("got:\n".Dumper([$response]), locus => $self->{REQ});
 
     $self->{tests}++;
     my $ok = $self->assert($response);
@@ -520,10 +578,7 @@ sub send_and_expect {
     } else {
 	$self->{failures}++;
     }
-    if (my $fh = $self->{xscript}) {
-	print $fh "$self->{filename}:$self->{EXP}{BEG}-$self->{EXP}{END}: " .
-		   ($ok ? "OK" : "FAIL")."\n";
-    }
+    $self->transcript($ok ? "OK" : "FAIL", locus => $self->{EXP});
     return $ok
 }
 
@@ -558,9 +613,8 @@ sub send {
 	    if (exists($self->{stats}{$k})) {
 		unless ($self->check_expect($s->${ \$k }, $self->{stats}{$k})) {
 		    $ok = 0;
-		    if (my $fh = $self->{xscript}) {
-			print $fh "$self->{filename}:$self->{EXP}{BEG}-$self->{EXP}{END}: $k: expected value mismatch\n";
-		    }
+		    $self->transcript("$k: expected value mismatch",
+				      locus => $self->{EXP});
 		}
 	    }
 	}
@@ -572,21 +626,20 @@ sub send {
 		unless ($self->check_expect($s->sample($i),
 					    $self->{stats}{expect}{$i})) {
 		    $ok = 0;
-		    if (my $fh = $self->{xscript}) {
-			print $fh "$self->{filename}:$self->{EXP}{BEG}-$self->{EXP}{END}: element $i: expected value mismatch\n";
-		    }
+		    $self->transcript("element $i: expected value mismatch",
+				      locus => $self->{EXP});
 		}
 	    }
 	}
 
 	$self->{failures}++ unless $ok;
 
-	if (my $fh = $self->{xscript}) {
-	    printf $fh "min=%f, avg=%f, max=%f, stddev=%f\n", $s->min, $s->avg, $s->max, $s->stddev;
+	if ($self->{xscript}) {
+	    $self->transcript(sprintf("min=%f, avg=%f, max=%f, stddev=%f",
+				      $s->min, $s->avg, $s->max, $s->stddev));
 	    local $Data::Dumper::Sortkeys=1;
-	    print $fh Dumper([$s->samples]);
-	    print $fh "$self->{filename}:$self->{EXP}{BEG}-$self->{EXP}{END}: " .
-		($ok ? "OK" : "FAIL")."\n";
+	    $self->transcript(Dumper([$s->samples]));
+	    $self->transcript($ok ? "OK" : "FAIL", locus => $self->{EXP});
 	}
     } else {
 	$self->send_and_expect($lst, $host);
@@ -648,8 +701,15 @@ sub assert {
 
 sub parse {
     my $self = shift;
-    while (!$self->{eof}) {
-	$self->parse_req;
+    eval {
+	while (!$self->{eof}) {
+	    $self->parse_req;
+	}
+    };
+    if ($@) {
+	print STDERR "$@";
+	print STDERR "Abnormal termination\n";
+	$self->{failures}++;
     }
 }
 
@@ -683,6 +743,11 @@ sub parse_req {
 	    sleep $1;
 	    next;
 	}
+	if (/^echo\s+(.+)/) {
+	    print STDERR "# ".$self->expandvars($1)."\n";
+	    next;
+	}
+
 	if (/^zonefile/) {
 	    $self->parse_zonefile;
 	    next;
@@ -692,7 +757,7 @@ sub parse_req {
 	    $self->parse_control_backends($1, $2);
 	    next;
 	}
-	
+
 	if (s/^stats//) {
 	    foreach my $k (qw(samples min max avg stddev index)) {
 		if (s{\s+$k = (?:
@@ -808,6 +873,13 @@ sub jsoncmp {
 
 sub parse_control_backends {
     my ($self, $ls, $sv) = @_;
+
+    if ($verbose) {
+	print "querying control interface\n";
+    }
+
+    $self->transcript("querying control interface");
+
     my $fh = $self->{fh};
     $self->{tests}++;
     my @exp;
@@ -817,18 +889,20 @@ sub parse_control_backends {
 	last if /^end$/;
 	push @exp, $self->expandvars($_)
     }
+
     my $ctl = PoundControl->new();
     my $belist = $ctl->backends($ls, $sv, sort => 1);
     my $json = JSON->new->boolean_values(0,1);
     my $exp = $json->decode(join(' ', @exp));
 
-    unless (jsoncmp($exp, $belist)) {
+    if (jsoncmp($exp, $belist)) {
+	$self->transcript_ok;
+    } else {
 	$self->{failures}++;
-	if (my $fh = $self->{xscript}) {
-	    print $fh "backend listings don't match\n";
-	    print $fh "exp: " . $json->pretty->encode($exp) . "\n";
-	    print $fh "got: " . $json->pretty->encode($belist) . "\n";
-	}
+	$self->transcript_ml("backend listings don't match",
+			     "exp: " . $json->canonical->pretty->encode($exp),
+			     "got: " . $json->canonical->pretty->encode($belist));
+	$self->transcript_fail;
     }
 }
 
@@ -1001,35 +1075,33 @@ sub runcom {
 	$code = $status_codes{$pid} >> 8;
     }
 
-    if (my $fh = $self->{xscript}) {
-	print $fh "Command: " . $self->{RUNCOM}{command} . "\n";
-	print $fh "Status code: $code\n";
-	print $fh "Stdout:\n";
-	print $fh $data{$child_stdout};
-	print $fh "\nEnd\n";
-	print $fh "Stderr:\n";
-	print $fh $data{$child_stderr};
-	print $fh "\nEnd\n";
-    }
+    $self->transcript_ml(
+	"Command: " . $self->{RUNCOM}{command},
+	"Status code: $code",
+	"Stdout:",
+	$data{$child_stdout},
+	"End",
+	"Stderr:",
+	$data{$child_stderr},
+	"End"
+    );
 
     $self->{tests}++;
+    my $ok = 1;
+
     if (exists($self->{RUNCOM}{status}) && $code != $self->{RUNCOM}{status}) {
 	$self->{failures}++;
-	if (my $fh = $self->{xscript}) {
-	    print $fh "$self->{filename}:$self->{RUNCOM}{BEG}-$self->{RUNCOM}{END}: exit code differs\n";
-	    return 0;
-	}
+	$self->transcript("exit code differs", locus => $self->{RUNCOM});
+	$ok = 0;
     }
 
     if (exists($self->{RUNCOM}{stdout})) {
 	my $s = join("\n", @{$self->{RUNCOM}{stdout}});
 	if ($data{$child_stdout} !~ m{$s}ms) {
 	    $self->{failures}++;
-	    if (my $fh = $self->{xscript}) {
-		print $fh "$self->{filename}:$self->{RUNCOM}{BEG}-$self->{RUNCOM}{END}: stdout differs\n";
-		print $fh "rx: $s\n";
-		return 0;
-	    }
+	    $self->transcript("stdout differs:\nrx: {$s}",
+			      locus => $self->{RUNCOM});
+	    $ok = 0;
 	}
     }
 
@@ -1037,15 +1109,14 @@ sub runcom {
 	my $s = join("\n", @{$self->{RUNCOM}{stderr}});
 	if ($data{$child_stderr} !~ m{$s}ms) {
 	    $self->{failures}++;
-	    if (my $fh = $self->{xscript}) {
-		print $fh "$self->{filename}:$self->{RUNCOM}{BEG}-$self->{RUNCOM}{END}: stderr differs\n";
-		print $fh "rx: $s\n";
-		return 0;
-	    }
+	    $self->transcript("stderr differs:\nrx: {$s}",
+			      locus => $self->{RUNCOM});
+	    $ok = 0;
 	}
     }
 
-    return 1
+    $self->transcript($ok ? "OK" : "FAIL");
+    return $ok
 }
 
 sub parse_expect {
@@ -1082,12 +1153,16 @@ sub parse_expect {
 sub replvar {
     my ($self, $var) = @_;
     if ($var =~ m{(LISTENER|BACKEND)(\d+)?(?::(PORT|IP))?}) {
-	my $var = ($1 eq 'LISTENER') ? $listeners : $backends;
+	my $list = ($1 eq 'LISTENER') ? $listeners : $backends;
 	my $meth = 'address';
 	if (defined($3)) {
 	    $meth = ($3 eq 'PORT') ? 'port' : 'host';
 	}
-	return $var->get($2//0)->${ \$meth };
+	if (my $obj = $list->get($2//0)) {
+	    return $obj->${ \$meth };
+	} else {
+	    $self->syntax_error("unrecognized variable $var");
+	}
     }
     return '${' . $var . '}';
 }
@@ -1321,7 +1396,8 @@ sub set_pass_fd {
 sub send_fd {
     my $self = shift;
     croak "not applicable" unless $self->pass_fd;
-    accept(my $fh, $self->pass_fd);
+    accept(my $fh, $self->pass_fd)
+	or croak "accept on pass_fd failed: $!";
     IO::FDPass::send(fileno($fh), $self->fd)
 	or croak "failed to pass socket: $!";
     close $self->socket_handle;
@@ -1420,7 +1496,7 @@ sub read_and_process {
 	    $lst->listen;
 	    while (1) {
 		my $fh;
-		accept($fh, $lst->socket_handle);
+		accept($fh, $lst->socket_handle) or threads->exit();
 		process_http_request($fh, $lst)
 	    }
 	}, $lst)->detach;
@@ -1601,7 +1677,6 @@ use Carp;
 use IPC::Open3;
 use IO::Select;
 use Symbol 'gensym';
-use JSON;
 use Data::Dumper;
 
 sub new {
@@ -1790,6 +1865,12 @@ the statements described in the B<DNS Statements> section (see below).
 If B<Net::DNS::Nameserver> perl module is not available and B<poundharness>
 is given this option, it will exit with status code 77.
 
+To avoid spurious failures, before actually using this functionality
+B<poundharness> performs a self-test, consisting in querying a SOA record of
+a mock DNS zone, using external helper program B<getsoa>.  If the test
+returns the correct data (indicating thereby that the trick with B<fakedns>
+works), B<poundharness> continues.  Otherwise, it exists with status code 77.
+
 =item B<--include-dir=>I<DIR>
 
 Look for relative file names in I<DIR>.  In particular, relative file names
@@ -2008,7 +2089,7 @@ itself.  Only attributes present in the expectation are compared.
 =item backends I<LN> I<SN>
 
 Query for backends in service I<SN> of listener I<LN>.
-The returned backend array is sorted by weight, priority and address. 
+The returned backend array is sorted by weight, priority and address.
 
 =back
 
@@ -2028,6 +2109,11 @@ Use it for testing dynamic backends.
 
 Pause execution for I<N> seconds.
 
+=item B<echo> I<WORD> [I<WORD>...]
+
+Expand variables (see B<HTTP send/expect> above) in I<WORD>s and output
+the result on standard error, prefixed with the B<#> sign.
+
 =head2 DNS Statements
 
 The following keywords are designed for testing dynamic DNS-based backends.
diff --git a/tests/prio.at b/tests/prio.at
index 424d487..980b23f 100644
--- a/tests/prio.at
+++ b/tests/prio.at
@@ -22,7 +22,7 @@ PT_CHECK(
 		Backend
 			Address
 			Port
-			Priority 0
+			Priority 2
 		End
 		Backend
 			Address
@@ -32,18 +32,7 @@ PT_CHECK(
 	End
 End
 ],
-[#
-# Let N be the number of backends and Wi weight (priority) of the i-th
-# backend.  Then, assuming 0-based weights, number of samples necessary
-# to finish a round-robin cycle is N+Sum(W).  Number of requests served
-# by backend j during the cycle is
-#
-#                    Wj + 1
-#                  ----------
-#                  N + Sum(W)
-#
-# In this test, N=2, Sum(W)=8, so that:
-stats samples=10 index=1 avg=0.9
+[stats samples=10 index=1 avg=0.8
 
 GET /echo/foo
 end
@@ -74,8 +63,7 @@ PT_CHECK(
 	End
 End
 ],
-[# N=2, Sum(W)=23, number of requests for backend 1 is 8/23:
-stats samples=23 index=1 avg=0.348
+[stats samples=100 index=1 avg=0.35
 
 GET /echo/foo
 end
@@ -101,9 +89,7 @@ PT_CHECK(
 End
 ],
 [# Default priority is 5
-# N=2, Sum(W)=16
-#
-stats samples=16 index=1 avg=0.625
+stats samples=100 index=1 avg=0.64
 
 GET /echo/foo
 end
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 69128d5..b6b92f2 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -153,4 +153,7 @@ m4_include([sessctl.at])
 
 AT_BANNER([Dynamic backends])
 m4_include([dyn_a.at])
+m4_include([dyn_a_ttl.at])
 m4_include([dyn_srv.at])
+m4_include([dyn_srv_0.at])
+m4_include([dyn_srv_ttl.at])
