BUG/MEDIUM: server: do not auto insert a dynamic server in px addr_node

Until then, the servers were automatically attached on their creation
into the proxy addr_node tree via _srv_parse_init. In case of an invalid
dynamic server which is instantly freed, no detach operation was made
leaving a NULL server in the tree.

Change this mode of operation by marking the attach operation as
optional in _srv_parse_init. This operation is not conduct for a dynamic
server. The server is attached only at the end of the CLI handler when
it is marked as valid.

This must be backported up to 2.4.

(cherry picked from commit 8ff0434b61d44e8c62f00744ebf7bfb0b76b15c0)
Signed-off-by: Willy Tarreau <w@1wt.eu>
diff --git a/src/server.c b/src/server.c
index 7d7953e..eb80104 100644
--- a/src/server.c
+++ b/src/server.c
@@ -198,9 +198,12 @@
 }
 
 /*
- * Must be called with the server lock held, and will write-lock the proxy.
+ * Must be called with the server lock held. The server is first removed from
+ * the proxy tree if it was already attached. If <reattach> is true, the server
+ * will then be attached in the proxy tree. The proxy lock is held to
+ * manipulate the tree.
  */
-static void srv_set_addr_desc(struct server *s)
+static void srv_set_addr_desc(struct server *s, int reattach)
 {
 	struct proxy *p = s->proxy;
 	char *key;
@@ -222,10 +225,12 @@
 
 	s->addr_node.key = key;
 
-	if (s->addr_node.key) {
-		HA_RWLOCK_WRLOCK(PROXY_LOCK, &p->lock);
-		ebis_insert(&p->used_server_addr, &s->addr_node);
-		HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &p->lock);
+	if (reattach) {
+		if (s->addr_node.key) {
+			HA_RWLOCK_WRLOCK(PROXY_LOCK, &p->lock);
+			ebis_insert(&p->used_server_addr, &s->addr_node);
+			HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &p->lock);
+		}
 	}
 }
 
@@ -2478,9 +2483,14 @@
 
 		newsrv->addr = *sk;
 		newsrv->svc_port = port;
-		// we don't need to lock the server here, because
-		// we are in the process of initializing
-		srv_set_addr_desc(newsrv);
+		/*
+		 * we don't need to lock the server here, because
+		 * we are in the process of initializing.
+		 *
+		 * Note that the server is not attached into the proxy tree if
+		 * this is a dynamic server.
+		 */
+		srv_set_addr_desc(newsrv, !(parse_flags & SRV_PARSE_DYNAMIC));
 
 		if (!newsrv->srvrq && !newsrv->hostname && !protocol_by_family(newsrv->addr.ss_family)) {
 			memprintf(errmsg, "Unknown protocol family %d '%s'",
@@ -2941,7 +2951,7 @@
 		break;
 	};
 	srv_set_dyncookie(s);
-	srv_set_addr_desc(s);
+	srv_set_addr_desc(s, 1);
 
 	return 0;
 }
@@ -3198,7 +3208,7 @@
 		/* force connection cleanup on the given server */
 		srv_cleanup_connections(s);
 		srv_set_dyncookie(s);
-		srv_set_addr_desc(s);
+		srv_set_addr_desc(s, 1);
 	}
 	if (updater)
 		chunk_appendf(msg, " by '%s'", updater);
@@ -3772,7 +3782,7 @@
 	return return_code;
 out:
 	srv_set_dyncookie(srv);
-	srv_set_addr_desc(srv);
+	srv_set_addr_desc(srv, 1);
 	return return_code;
 }
 
@@ -4480,6 +4490,7 @@
 	/* insert the server in the backend trees */
 	eb32_insert(&be->conf.used_server_id, &srv->conf.id);
 	ebis_insert(&be->conf.used_server_name, &srv->conf.name);
+	ebis_insert(&be->used_server_addr, &srv->addr_node);
 
 	thread_release();