Index: include/ap_mmn.h
===================================================================
--- include/ap_mmn.h	(revision 1823929)
+++ include/ap_mmn.h	(working copy)
@@ -562,6 +562,7 @@
  * 20171014.2 (2.5.1-dev)  Add "use_specific_errors" to listen_rec and 
  *                         ap_accept_error_is_nonfatal()
  * 20171014.3 (2.5.1-dev)  AP_DECLARE ap_parse_vhost_addrs() as public 
+ * 20171014.4 (2.5.1-dev)  Adding child, sibling and parent pointers to server_rec
  */
 
 #define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */
Index: include/http_config.h
===================================================================
--- include/http_config.h	(revision 1823929)
+++ include/http_config.h	(working copy)
@@ -950,6 +950,7 @@
 #define  NOT_IN_FILES           0x10 /**< Forbidden in &lt;Files&gt; or &lt;If&gt;*/
 #define  NOT_IN_HTACCESS        0x20 /**< Forbidden in .htaccess files */
 #define  NOT_IN_PROXY           0x40 /**< Forbidden in &lt;Proxy&gt; */
+#define  BUT_IN_HOSTGROUP       0x80 /**< Allowed in &lt;HostGroup&gt; */
 /** Forbidden in &lt;Directory&gt;/&lt;Location&gt;/&lt;Files&gt;&lt;If&gt;*/
 #define  NOT_IN_DIR_LOC_FILE    (NOT_IN_DIRECTORY|NOT_IN_LOCATION|NOT_IN_FILES)
 /** Forbidden in &lt;Directory&gt;/&lt;Location&gt;/&lt;Files&gt;&lt;If&gt;&lt;Proxy&gt;*/
@@ -956,6 +957,7 @@
 #define  NOT_IN_DIR_CONTEXT     (NOT_IN_LIMIT|NOT_IN_DIR_LOC_FILE|NOT_IN_PROXY)
 /** Forbidden in &lt;VirtualHost&gt;/&lt;Limit&gt;/&lt;Directory&gt;/&lt;Location&gt;/&lt;Files&gt;/&lt;If&gt;&lt;Proxy&gt;*/
 #define  GLOBAL_ONLY            (NOT_IN_VIRTUALHOST|NOT_IN_DIR_CONTEXT)
+#define  GLOBAL_AND_HOSTGROUP   (GLOBAL_ONLY|BUT_IN_HOSTGROUP)
 
 /** @} */
 
@@ -1164,16 +1166,29 @@
  * Setup a virtual host
  * @param p The pool to allocate all memory from
  * @param hostname The hostname of the virtual hsot
- * @param main_server The main server for this Apache configuration
+ * @param parent The main server for this Apache configuration or a host group
  * @param ps Place to store the new server_rec
  * return Error string on error, NULL on success
  */
 AP_CORE_DECLARE(const char *) ap_init_virtual_host(apr_pool_t *p,
                                                    const char *hostname,
-                                                   server_rec *main_server,
+                                                   server_rec *parent,
                                                    server_rec **ps);
 
 /**
+ * Setup a host group
+ * @param p The pool to allocate all memory from
+ * @param groupname The name of the group, must be unique
+ * @param parent The main server for this Apache configuration
+ * @param ps Place to store the new server_rec
+ * return Error string on error, NULL on success
+ */
+AP_CORE_DECLARE(const char *) ap_init_host_group(apr_pool_t *p,
+                                                 const char *groupname,
+                                                 server_rec *parent,
+                                                 server_rec **ps);
+
+/**
  * Process a config file for Apache
  * @param s The server rec to use for the command parms
  * @param fname The name of the config file
Index: include/httpd.h
===================================================================
--- include/httpd.h	(revision 1823929)
+++ include/httpd.h	(working copy)
@@ -1388,6 +1388,13 @@
      *  inherited (0) from the base server (either first
      *  server on the same IP:port or main server) */
     unsigned int keep_alive_timeout_set:1;
+    
+    /** This first server_rec defined inside this one */ 
+    server_rec *child;
+    /** The next server_rec with the same parent */
+    server_rec *sibling;
+    /** The parent server_rec, NULL for the base server */
+    server_rec *parent;
 };
 
 /**
Index: server/config.c
===================================================================
--- server/config.c	(revision 1823929)
+++ server/config.c	(working copy)
@@ -309,9 +309,9 @@
     return (ap_conf_vector_t *)conf_vector;
 }
 
-static ap_conf_vector_t *create_server_config(apr_pool_t *p, server_rec *s)
+static void init_server_config(apr_pool_t *p, server_rec *s)
 {
-    void **conf_vector = apr_pcalloc(p, sizeof(void *) * conf_vector_length);
+    void **conf_vector = (void **)s->module_config;
     module *modp;
 
     for (modp = ap_top_module; modp; modp = modp->next) {
@@ -318,8 +318,6 @@
         if (modp->create_server_config)
             conf_vector[modp->module_index] = (*modp->create_server_config)(p, s);
     }
-
-    return (ap_conf_vector_t *)conf_vector;
 }
 
 static void merge_server_configs(apr_pool_t *p, ap_conf_vector_t *base,
@@ -2271,44 +2269,46 @@
     return OK;
 }
 
+static void link_server_rec(server_rec *parent, server_rec *s);
+static server_rec *init_server_rec(apr_pool_t *p, server_rec *parent, process_rec *process);
+
 AP_CORE_DECLARE(const char *) ap_init_virtual_host(apr_pool_t *p,
                                                    const char *hostname,
-                                                   server_rec *main_server,
+                                                   server_rec *parent,
                                                    server_rec **ps)
 {
-    server_rec *s = (server_rec *) apr_pcalloc(p, sizeof(server_rec));
-
-    /* TODO: this crap belongs in http_core */
-    s->process = main_server->process;
-    s->server_admin = NULL;
-    s->server_hostname = NULL;
-    s->server_scheme = NULL;
-    s->error_fname = NULL;
-    s->timeout = 0;
-    s->keep_alive_timeout = 0;
-    s->keep_alive = -1;
-    s->keep_alive_max = -1;
-    s->error_log = main_server->error_log;
-    s->log.level = APLOG_UNSET;
-    s->log.module_levels = NULL;
-    /* useful default, otherwise we get a port of 0 on redirects */
-    s->port = main_server->port;
-    s->next = NULL;
-
+    server_rec *s = init_server_rec(p, parent, NULL);
+    const char *err;
+    
     s->is_virtual = 1;
-    s->names = apr_array_make(p, 4, sizeof(char **));
-    s->wild_names = apr_array_make(p, 4, sizeof(char **));
+    err = ap_parse_vhost_addrs(p, hostname, s);
+    if (!err) {
+        link_server_rec(parent, s);
+    }
+    *ps = s;
+    return err;    
+}
 
-    s->module_config = create_empty_config(p);
-    s->lookup_defaults = ap_create_per_dir_config(p);
-
-    s->limit_req_line = main_server->limit_req_line;
-    s->limit_req_fieldsize = main_server->limit_req_fieldsize;
-    s->limit_req_fields = main_server->limit_req_fields;
-
+AP_CORE_DECLARE(const char *) ap_init_host_group(apr_pool_t *p,
+                                                 const char *groupname,
+                                                 server_rec *parent,
+                                                 server_rec **ps)
+{
+    server_rec *s;
+    
+    for (s = parent->child; s; s = s->sibling) {
+        if (s->server_hostname 
+            && !apr_strnatcasecmp(groupname, s->server_hostname)) {
+            return apr_psprintf(p, "HostGroup/VirtualHost already defined: %s", groupname);
+        }
+    }
+    
+    s = init_server_rec(p, parent, NULL);
+    s->server_hostname = apr_pstrdup(p, groupname);
+    link_server_rec(parent, s);
     *ps = s;
-
-    return ap_parse_vhost_addrs(p, hostname, s);
+    
+    return NULL;
 }
 
 AP_DECLARE(struct ap_logconf *) ap_new_log_config(apr_pool_t *p,
@@ -2351,47 +2351,68 @@
     }
 }
 
-AP_DECLARE(void) ap_fixup_virtual_hosts(apr_pool_t *p, server_rec *main_server)
+static void merge_server_recs(apr_pool_t *p, server_rec *from, server_rec *to)
 {
-    server_rec *virt;
-    core_dir_config *dconf =
-        ap_get_core_module_config(main_server->lookup_defaults);
-    dconf->log = &main_server->log;
+    merge_server_configs(p, from->module_config, to);
+    to->lookup_defaults = ap_merge_per_dir_configs(p, from->lookup_defaults,
+                                                   to->lookup_defaults);
+    
+    if (to->server_admin == NULL)
+        to->server_admin = from->server_admin;
+    
+    if (to->timeout == 0)
+        to->timeout = from->timeout;
+    
+    if (to->keep_alive_timeout == 0)
+        to->keep_alive_timeout = from->keep_alive_timeout;
+    
+    if (to->keep_alive == -1)
+        to->keep_alive = from->keep_alive;
+    
+    if (to->keep_alive_max == -1)
+        to->keep_alive_max = from->keep_alive_max;
+    
+    ap_merge_log_config(&from->log, &to->log);
 
-    for (virt = main_server->next; virt; virt = virt->next) {
-        merge_server_configs(p, main_server->module_config, virt);
+    /* Certain things are only inherited from hostgroups to descendants */
+    if (from->parent) {
+        if (!to->server_hostname)
+            to->server_hostname = from->server_hostname;
 
-        virt->lookup_defaults =
-            ap_merge_per_dir_configs(p, main_server->lookup_defaults,
-                                     virt->lookup_defaults);
+        if (!to->names || apr_is_empty_array(to->names)) 
+            to->names = from->names;
 
-        if (virt->server_admin == NULL)
-            virt->server_admin = main_server->server_admin;
+        if (!to->wild_names || apr_is_empty_array(to->wild_names)) 
+            to->wild_names = from->wild_names;
+    }
+}
 
-        if (virt->timeout == 0)
-            virt->timeout = main_server->timeout;
+static void fixup_host_groups_inside(apr_pool_t *p, server_rec *s)
+{
+    server_rec *child;
+    
+    /* Just in case we ever want to nest these, do recursion. */
+    for (child = s->child; child; child = child->sibling) {
+        merge_server_recs(p, s, child);            
+        fixup_host_groups_inside(p, child);
+    }
+    ap_core_reorder_directories(p, s);
+}
 
-        if (virt->keep_alive_timeout == 0)
-            virt->keep_alive_timeout = main_server->keep_alive_timeout;
+AP_DECLARE(void) ap_fixup_virtual_hosts(apr_pool_t *p, server_rec *main_server)
+{
+    server_rec *virt;
+    core_dir_config *dconf;
+    
+    dconf = ap_get_core_module_config(main_server->lookup_defaults);
+    dconf->log = &main_server->log;
 
-        if (virt->keep_alive == -1)
-            virt->keep_alive = main_server->keep_alive;
-
-        if (virt->keep_alive_max == -1)
-            virt->keep_alive_max = main_server->keep_alive_max;
-
-        ap_merge_log_config(&main_server->log, &virt->log);
-
+    fixup_host_groups_inside(p, main_server);
+    
+    for (virt = main_server->next; virt; virt = virt->next) {
         dconf = ap_get_core_module_config(virt->lookup_defaults);
         dconf->log = &virt->log;
-
-        /* XXX: this is really something that should be dealt with by a
-         * post-config api phase
-         */
-        ap_core_reorder_directories(p, virt);
     }
-
-    ap_core_reorder_directories(p, main_server);
 }
 
 /*****************************************************************
@@ -2405,46 +2426,93 @@
     ap_init_vhost_config(p);
 }
 
-static server_rec *init_server_config(process_rec *process, apr_pool_t *p)
+static void link_server_rec(server_rec *parent, server_rec *s)
 {
+    server_rec **pchild, *root;
+
+    pchild = &parent->child;
+    while (*pchild) {
+        pchild = &((*pchild)->sibling);
+    }
+    *pchild = s;
+        
+    if (s->is_virtual) {
+        /* VirtualHosts immediately apppear on the server->next list of the root server */
+        root = parent;
+        while (root->parent) {
+            root = root->parent;
+        }
+        s->next = root->next;
+        root->next = s;
+    }
+}
+
+static server_rec *init_server_rec(apr_pool_t *p, server_rec *parent, process_rec *process)
+{
     apr_status_t rv;
     server_rec *s = (server_rec *) apr_pcalloc(p, sizeof(server_rec));
 
-    apr_file_open_stderr(&s->error_log, p);
-    s->process = process;
-    s->port = 0;
-    s->server_admin = DEFAULT_ADMIN;
-    s->server_hostname = NULL;
-    s->server_scheme = NULL;
-    s->error_fname = DEFAULT_ERRORLOG;
-    s->log.level = DEFAULT_LOGLEVEL;
-    s->log.module_levels = NULL;
-    s->limit_req_line = DEFAULT_LIMIT_REQUEST_LINE;
-    s->limit_req_fieldsize = DEFAULT_LIMIT_REQUEST_FIELDSIZE;
-    s->limit_req_fields = DEFAULT_LIMIT_REQUEST_FIELDS;
-    s->timeout = apr_time_from_sec(DEFAULT_TIMEOUT);
-    s->keep_alive_timeout = apr_time_from_sec(DEFAULT_KEEPALIVE_TIMEOUT);
-    s->keep_alive_max = DEFAULT_KEEPALIVE;
-    s->keep_alive = 1;
-    s->next = NULL;
-    s->addrs = apr_pcalloc(p, sizeof(server_addr_rec));
+    s->module_config = create_empty_config(p);
+    s->lookup_defaults = create_default_per_dir_config(p);
+    
+    if (parent) {
+        s->process = parent->process;
+        s->parent = parent;
+        
+        /* Mark some fields unset, so config is retrieved at parent */
+        s->error_log = parent->error_log;
+        s->log.level = APLOG_UNSET;
+        
+        /* Inherit some fields right away */
+        /* FIXME: should this not all happen at server_rec merging when all config
+         * directives for the parent server are in effect? */
+        s->limit_req_line = parent->limit_req_line;
+        s->limit_req_fieldsize = parent->limit_req_fieldsize;
+        s->limit_req_fields = parent->limit_req_fields;    
 
-    /* NOT virtual host; don't match any real network interface */
-    rv = apr_sockaddr_info_get(&s->addrs->host_addr,
-                               NULL, APR_UNSPEC, 0, 0, p);
-    if (rv != APR_SUCCESS) {
-        /* should we test here for rv being an EAIERR? */
-        ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, rv, NULL, APLOGNO(00530)
-                     "initialisation: bug or getaddrinfo fail");
-        return NULL;
+        /* useful default, otherwise we get a port of 0 on redirects */
+        s->port = parent->port;
+        s->keep_alive_max = -1;
+        s->keep_alive = -1;
+        
+        /* only child server_recs have these */
+        s->names = apr_array_make(p, 4, sizeof(char **));
+        s->wild_names = apr_array_make(p, 4, sizeof(char **));
     }
+    else {
+        apr_file_open_stderr(&s->error_log, p);
+        s->process = process;
+        
+        s->server_admin = DEFAULT_ADMIN;
+        s->error_fname = DEFAULT_ERRORLOG;
+        s->log.level = DEFAULT_LOGLEVEL;
 
-    s->addrs->host_port = 0; /* matches any port */
-    s->addrs->virthost = ""; /* must be non-NULL */
-    s->names = s->wild_names = NULL;
+        s->limit_req_line = DEFAULT_LIMIT_REQUEST_LINE;
+        s->limit_req_fieldsize = DEFAULT_LIMIT_REQUEST_FIELDSIZE;
+        s->limit_req_fields = DEFAULT_LIMIT_REQUEST_FIELDS;
 
-    s->module_config = create_server_config(p, s);
-    s->lookup_defaults = create_default_per_dir_config(p);
+        s->timeout = apr_time_from_sec(DEFAULT_TIMEOUT);
+        s->keep_alive_timeout = apr_time_from_sec(DEFAULT_KEEPALIVE_TIMEOUT);
+        s->keep_alive_max = DEFAULT_KEEPALIVE;
+        s->keep_alive = 1;
+        
+        s->addrs = apr_pcalloc(p, sizeof(server_addr_rec));
+        /* NOT virtual host; don't match any real network interface */
+        rv = apr_sockaddr_info_get(&s->addrs->host_addr,
+                                   NULL, APR_UNSPEC, 0, 0, p);
+        if (rv != APR_SUCCESS) {
+            /* should we test here for rv being an EAIERR? */
+            ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, rv, NULL, APLOGNO(00530)
+                         "initialisation: bug or getaddrinfo fail");
+            return NULL;
+        }
+        
+        s->addrs->host_port = 0; /* matches any port */
+        s->addrs->virthost = ""; /* must be non-NULL */
+        s->names = s->wild_names = NULL;
+        
+        init_server_config(p, s);
+    }
 
     return s;
 }
@@ -2488,7 +2556,7 @@
 {
     const char *confname, *error;
     apr_pool_t *p = process->pconf;
-    server_rec *s = init_server_config(process, p);
+    server_rec *s = init_server_rec(p, NULL, process);
     if (s == NULL) {
         return s;
     }
Index: server/core.c
===================================================================
--- server/core.c	(revision 1823929)
+++ server/core.c	(working copy)
@@ -1285,6 +1285,12 @@
                            " cannot occur within <VirtualHost> section", NULL);
     }
 
+    if ((forbidden & NOT_IN_VIRTUALHOST) && cmd->server->parent
+        && !(forbidden & BUT_IN_HOSTGROUP)) {
+        return apr_pstrcat(cmd->pool, cmd->cmd->name, gt,
+                           " cannot occur within <HostGroup> section", NULL);
+    }
+
     if ((forbidden & NOT_IN_DIR_CONTEXT) && cmd->limited != -1) {
         return apr_pstrcat(cmd->pool, cmd->cmd->name, gt,
                            " cannot occur within <Limit> or <LimitExcept> "
@@ -2854,12 +2860,12 @@
     return ap_exists_directive(cmd->temp_pool, name);
 }
 
-/* httpd.conf commands... beginning with the <VirtualHost> business */
+/* httpd.conf commands... beginning with the <HostGroup> business */
 
-static const char *virtualhost_section(cmd_parms *cmd, void *dummy,
-                                       const char *arg)
+static const char *hostgroup_section(cmd_parms *cmd, void *dummy,
+                                     const char *arg)
 {
-    server_rec *main_server = cmd->server, *s;
+    server_rec *parent = cmd->server, *s;
     const char *errmsg;
     const char *endp = ap_strrchr_c(arg, '>');
     apr_pool_t *p = cmd->pool;
@@ -2879,23 +2885,68 @@
         return missing_container_arg(cmd);
     }
 
+    /* We could allow it inside itself - one day maybe. */
+    if (parent->parent) {
+        return "<HostGroup> doesn't nest!";
+    }
+
+    errmsg = ap_init_host_group(p, arg, parent, &s);
+    if (errmsg) {
+        return errmsg;
+    }
+
+    s->defn_name = cmd->directive->filename;
+    s->defn_line_number = cmd->directive->line_num;
+
+    cmd->server = s;
+
+    errmsg = ap_walk_config(cmd->directive->first_child, cmd,
+                            s->lookup_defaults);
+
+    cmd->server = parent;
+
+    return errmsg;
+}
+
+static const char *virtualhost_section(cmd_parms *cmd, void *dummy,
+                                       const char *arg)
+{
+    server_rec *parent = cmd->server, *s;
+    const char *errmsg;
+    const char *endp = ap_strrchr_c(arg, '>');
+    apr_pool_t *p = cmd->pool;
+
+    if (!parent->parent || parent->is_virtual) {
+        errmsg = ap_check_cmd_context(cmd, GLOBAL_AND_HOSTGROUP);
+        if (errmsg != NULL) {
+            return errmsg;
+        }
+    }
+
+    if (endp == NULL) {
+        return unclosed_directive(cmd);
+    }
+
+    arg = apr_pstrndup(cmd->temp_pool, arg, endp - arg);
+
+    if (!arg[0]) {
+        return missing_container_arg(cmd);
+    }
+
     /* FIXME: There's another feature waiting to happen here -- since you
         can now put multiple addresses/names on a single <VirtualHost>
         you might want to use it to group common definitions and then
         define other "subhosts" with their individual differences.  But
         personally I'd rather just do it with a macro preprocessor. -djg */
-    if (main_server->is_virtual) {
+    if (parent->is_virtual) {
         return "<VirtualHost> doesn't nest!";
     }
 
-    errmsg = ap_init_virtual_host(p, arg, main_server, &s);
+    errmsg = ap_init_virtual_host(p, arg, parent, &s);
     if (errmsg) {
         return errmsg;
     }
 
-    s->next = main_server->next;
-    main_server->next = s;
-
     s->defn_name = cmd->directive->filename;
     s->defn_line_number = cmd->directive->line_num;
 
@@ -2904,7 +2955,7 @@
     errmsg = ap_walk_config(cmd->directive->first_child, cmd,
                             s->lookup_defaults);
 
-    cmd->server = main_server;
+    cmd->server = parent;
 
     return errmsg;
 }
@@ -4450,6 +4501,9 @@
 AP_INIT_RAW_ARGS("<Location", urlsection, NULL, RSRC_CONF,
   "Container for directives affecting resources accessed through the "
   "specified URL paths"),
+AP_INIT_RAW_ARGS("<HostGroup", hostgroup_section, NULL, RSRC_CONF,
+  "Container to map directives all contained virtual hosts, takes one "
+  "unique name that defaults to ServerName of all contained hosts."),
 AP_INIT_RAW_ARGS("<VirtualHost", virtualhost_section, NULL, RSRC_CONF,
   "Container to map directives to a particular virtual host, takes one or "
   "more host addresses"),
