Implement DNS SRV record into the bridge driver

Hi,
this is the fifth version of my SRV record for DNSMasq patch rebased
for the current codebase to the bridge driver and libvirt XML file to
include support for the SRV records in the DNS. The syntax is based on
DNSMasq man page and tests for both xml2xml and xml2argv were added as
well. There are some things written a better way in comparison with
version 4, mainly there's no hack in tests/networkxml2argvtest.c and
also the xPath context is changed to use a simpler query using the
virXPathInt() function relative to the current node.

Also, the patch is also fixing the networkxml2argv test to pass both
checks, i.e. both unit tests and also syntax check.

Please review,
Michal

Signed-off-by: Michal Novotny <minovotn@redhat.com>
This commit is contained in:
Michal Novotny 2012-01-02 15:23:54 +01:00 committed by Daniel Veillard
parent baade4cd2b
commit 973af2362c
15 changed files with 423 additions and 9 deletions

View File

@ -345,6 +345,7 @@
&lt;domain name="example.com"/&gt;
&lt;dns&gt;
&lt;txt name="example" value="example value" /&gt;
&lt;srv service='name' protocol='tcp' domain='test-domain-name' target='.' port='1024' priority='10' weight='10'/&gt;
&lt;host ip='192.168.122.2'&gt;
&lt;hostname&gt;myhost&lt;/hostname&gt;
&lt;hostname&gt;myhostalias&lt;/hostname&gt;
@ -396,6 +397,17 @@
<span class="since">Since 0.9.3</span>
</dd>
</dl>
<dl>
<dt><code>srv</code></dt>
<dd>The <code>dns</code> element can have also 0 or more <code>srv</code>
record elements. Each <code>srv</code> record element defines a DNS SRV record
and has 2 mandatory and 5 optional attributes. The mandatory attributes
are service name and protocol (tcp, udp) and the optional attributes are
target, port, priority, weight and domain as defined in DNS server SRV
RFC (RFC 2782).
<span class="since">Since 0.9.9</span>
</dd>
</dl>
</dd>
<dt><code>ip</code></dt>
<dd>The <code>address</code> attribute defines an IPv4 address in

View File

@ -137,6 +137,19 @@
<attribute name="value"><text/></attribute>
</element>
</zeroOrMore>
<zeroOrMore>
<element name="srv">
<attribute name="service"><text/></attribute>
<attribute name="protocol"><ref name="protocol"/></attribute>
<optional>
<attribute name="domain"><ref name="dnsName"/></attribute>
<attribute name="target"><text/></attribute>
<attribute name="port"><ref name="unsignedShort"/></attribute>
<attribute name="priority"><ref name="unsignedShort"/></attribute>
<attribute name="weight"><ref name="unsignedShort"/></attribute>
</optional>
</element>
</zeroOrMore>
<zeroOrMore>
<element name="host">
<attribute name="ip"><ref name="ipv4Addr"/></attribute>
@ -216,11 +229,4 @@
</interleave>
</element>
</define>
<define name='addr-family'>
<data type='string'>
<param name="pattern">(ipv4)|(ipv6)</param>
</data>
</define>
</grammar>

View File

@ -95,4 +95,21 @@
<param name="minInclusive">1</param>
</data>
</define>
<define name='unsignedShort'>
<data type='integer'>
<param name="minInclusive">0</param>
<param name="maxInclusive">65535</param>
</data>
</define>
<define name='protocol'>
<data type='string'>
<param name='pattern'>(tcp)|(udp)</param>
</data>
</define>
<define name='addr-family'>
<data type='string'>
<param name="pattern">(ipv4)|(ipv6)</param>
</data>
</define>
</grammar>

View File

@ -138,6 +138,15 @@ static void virNetworkDNSDefFree(virNetworkDNSDefPtr def)
}
}
VIR_FREE(def->hosts);
if (def->nsrvrecords) {
while (def->nsrvrecords--) {
VIR_FREE(def->srvrecords[def->nsrvrecords].domain);
VIR_FREE(def->srvrecords[def->nsrvrecords].service);
VIR_FREE(def->srvrecords[def->nsrvrecords].protocol);
VIR_FREE(def->srvrecords[def->nsrvrecords].target);
}
}
VIR_FREE(def->srvrecords);
VIR_FREE(def);
}
}
@ -552,9 +561,104 @@ error:
return ret;
}
static int
virNetworkDNSSrvDefParseXML(virNetworkDNSDefPtr def,
xmlNodePtr cur,
xmlXPathContextPtr ctxt)
{
char *domain;
char *service;
char *protocol;
char *target;
int port;
int priority;
int weight;
int ret = 0;
if (!(service = virXMLPropString(cur, "service"))) {
virNetworkReportError(VIR_ERR_XML_DETAIL,
"%s", _("Missing required service attribute in dns srv record"));
goto error;
}
if (strlen(service) > DNS_RECORD_LENGTH_SRV) {
char *name = NULL;
virAsprintf(&name, _("Service name is too long, limit is %d bytes"), DNS_RECORD_LENGTH_SRV);
virNetworkReportError(VIR_ERR_XML_DETAIL,
"%s", name);
free(name);
goto error;
}
if (!(protocol = virXMLPropString(cur, "protocol"))) {
virNetworkReportError(VIR_ERR_XML_DETAIL,
_("Missing required protocol attribute in dns srv record '%s'"), service);
goto error;
}
/* Check whether protocol value is the supported one */
if (STRNEQ(protocol, "tcp") && (STRNEQ(protocol, "udp"))) {
virNetworkReportError(VIR_ERR_XML_DETAIL,
_("Invalid protocol attribute value '%s'"), protocol);
goto error;
}
if (VIR_REALLOC_N(def->srvrecords, def->nsrvrecords + 1) < 0) {
virReportOOMError();
goto error;
}
def->srvrecords[def->nsrvrecords].service = service;
def->srvrecords[def->nsrvrecords].protocol = protocol;
def->srvrecords[def->nsrvrecords].domain = NULL;
def->srvrecords[def->nsrvrecords].target = NULL;
def->srvrecords[def->nsrvrecords].port = 0;
def->srvrecords[def->nsrvrecords].priority = 0;
def->srvrecords[def->nsrvrecords].weight = 0;
/* Following attributes are optional but we had to make sure they're NULL above */
if ((target = virXMLPropString(cur, "target")) && (domain = virXMLPropString(cur, "domain"))) {
xmlNodePtr save_ctxt = ctxt->node;
ctxt->node = cur;
if (virXPathInt("string(./@port)", ctxt, &port))
def->srvrecords[def->nsrvrecords].port = port;
if (virXPathInt("string(./@priority)", ctxt, &priority))
def->srvrecords[def->nsrvrecords].priority = priority;
if (virXPathInt("string(./@weight)", ctxt, &weight))
def->srvrecords[def->nsrvrecords].weight = weight;
ctxt->node = save_ctxt;
def->srvrecords[def->nsrvrecords].domain = domain;
def->srvrecords[def->nsrvrecords].target = target;
def->srvrecords[def->nsrvrecords].port = port;
def->srvrecords[def->nsrvrecords].priority = priority;
def->srvrecords[def->nsrvrecords].weight = weight;
}
def->nsrvrecords++;
goto cleanup;
error:
VIR_FREE(domain);
VIR_FREE(service);
VIR_FREE(protocol);
VIR_FREE(target);
ret = -1;
cleanup:
return ret;
}
static int
virNetworkDNSDefParseXML(virNetworkDNSDefPtr *dnsdef,
xmlNodePtr node)
xmlNodePtr node,
xmlXPathContextPtr ctxt)
{
xmlNodePtr cur;
int ret = -1;
@ -598,6 +702,11 @@ virNetworkDNSDefParseXML(virNetworkDNSDefPtr *dnsdef,
def->ntxtrecords++;
name = NULL;
value = NULL;
} else if (cur->type == XML_ELEMENT_NODE &&
xmlStrEqual(cur->name, BAD_CAST "srv")) {
ret = virNetworkDNSSrvDefParseXML(def, cur, ctxt);
if (ret < 0)
goto error;
} else if (cur->type == XML_ELEMENT_NODE &&
xmlStrEqual(cur->name, BAD_CAST "host")) {
ret = virNetworkDNSHostsDefParseXML(def, cur);
@ -887,7 +996,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
dnsNode = virXPathNode("./dns", ctxt);
if (dnsNode != NULL) {
if (virNetworkDNSDefParseXML(&def->dns, dnsNode) < 0)
if (virNetworkDNSDefParseXML(&def->dns, dnsNode, ctxt) < 0)
goto error;
}
@ -1146,6 +1255,27 @@ virNetworkDNSDefFormat(virBufferPtr buf,
def->txtrecords[i].value);
}
for (i = 0 ; i < def->nsrvrecords ; i++) {
if (def->srvrecords[i].service && def->srvrecords[i].protocol) {
virBufferAsprintf(buf, " <srv service='%s' protocol='%s'",
def->srvrecords[i].service,
def->srvrecords[i].protocol);
if (def->srvrecords[i].domain)
virBufferAsprintf(buf, " domain='%s'", def->srvrecords[i].domain);
if (def->srvrecords[i].target)
virBufferAsprintf(buf, " target='%s'", def->srvrecords[i].target);
if (def->srvrecords[i].port)
virBufferAsprintf(buf, " port='%d'", def->srvrecords[i].port);
if (def->srvrecords[i].priority)
virBufferAsprintf(buf, " priority='%d'", def->srvrecords[i].priority);
if (def->srvrecords[i].weight)
virBufferAsprintf(buf, " weight='%d'", def->srvrecords[i].weight);
virBufferAsprintf(buf, "/>\n");
}
}
if (def->nhosts) {
int ii, j;

View File

@ -24,6 +24,8 @@
#ifndef __NETWORK_CONF_H__
# define __NETWORK_CONF_H__
#define DNS_RECORD_LENGTH_SRV (512 - 30) /* Limit minus overhead as mentioned in RFC-2782 */
# include <libxml/parser.h>
# include <libxml/tree.h>
# include <libxml/xpath.h>
@ -69,6 +71,18 @@ struct _virNetworkDNSTxtRecordsDef {
char *value;
};
typedef struct _virNetworkDNSSrvRecordsDef virNetworkDNSSrvRecordsDef;
typedef virNetworkDNSSrvRecordsDef *virNetworkDNSSrvRecordsDefPtr;
struct _virNetworkDNSSrvRecordsDef {
char *domain;
char *service;
char *protocol;
char *target;
int port;
int priority;
int weight;
};
struct _virNetworkDNSHostsDef {
virSocketAddr ip;
int nnames;
@ -82,6 +96,8 @@ struct _virNetworkDNSDef {
virNetworkDNSTxtRecordsDefPtr txtrecords;
unsigned int nhosts;
virNetworkDNSHostsDefPtr hosts;
unsigned int nsrvrecords;
virNetworkDNSSrvRecordsDefPtr srvrecords;
};
typedef struct _virNetworkDNSDef *virNetworkDNSDefPtr;

View File

@ -527,6 +527,49 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
virCommandAddArgPair(cmd, "--txt-record", record);
VIR_FREE(record);
}
for (i = 0; i < dns->nsrvrecords; i++) {
char *record = NULL;
char *recordPort = NULL;
char *recordPriority = NULL;
char *recordWeight = NULL;
if (dns->srvrecords[i].service && dns->srvrecords[i].protocol) {
if (dns->srvrecords[i].port) {
if (virAsprintf(&recordPort, "%d", dns->srvrecords[i].port) < 0) {
virReportOOMError();
goto cleanup;
}
}
if (dns->srvrecords[i].priority) {
if (virAsprintf(&recordPriority, "%d", dns->srvrecords[i].priority) < 0) {
virReportOOMError();
goto cleanup;
}
}
if (dns->srvrecords[i].weight) {
if (virAsprintf(&recordWeight, "%d", dns->srvrecords[i].weight) < 0) {
virReportOOMError();
goto cleanup;
}
}
if (virAsprintf(&record, "%s.%s.%s,%s,%s,%s,%s",
dns->srvrecords[i].service,
dns->srvrecords[i].protocol,
dns->srvrecords[i].domain ? dns->srvrecords[i].domain : "",
dns->srvrecords[i].target ? dns->srvrecords[i].target : "",
recordPort ? recordPort : "",
recordPriority ? recordPriority : "",
recordWeight ? recordWeight : "") < 0) {
virReportOOMError();
goto cleanup;
}
virCommandAddArgPair(cmd, "--srv-host", record);
VIR_FREE(record);
}
}
}
/*

View File

@ -0,0 +1,16 @@
/usr/sbin/dnsmasq \
--strict-order \
--bind-interfaces \
--conf-file= \
--except-interface lo \
--srv-host=name.tcp.,,,, \
--listen-address 192.168.122.1 \
--listen-address 192.168.123.1 \
--listen-address 2001:db8:ac10:fe01::1 \
--listen-address 2001:db8:ac10:fd01::1 \
--listen-address 10.24.10.1 \
--dhcp-range 192.168.122.2,192.168.122.254 \
--dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \
--dhcp-lease-max=253 \
--dhcp-no-override \
--dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile\

View File

@ -0,0 +1,26 @@
<network>
<name>default</name>
<uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
<forward dev='eth1' mode='nat'>
<interface dev='eth1'/>
</forward>
<bridge name='virbr0' stp='on' delay='0' />
<dns>
<srv service='name' protocol='tcp' />
</dns>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254' />
<host mac='00:16:3e:77:e2:ed' name='a.example.com' ip='192.168.122.10' />
<host mac='00:16:3e:3e:a9:1a' name='b.example.com' ip='192.168.122.11' />
</dhcp>
</ip>
<ip family='ipv4' address='192.168.123.1' netmask='255.255.255.0'>
</ip>
<ip family='ipv6' address='2001:db8:ac10:fe01::1' prefix='64'>
</ip>
<ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
</ip>
<ip family='ipv4' address='10.24.10.1'>
</ip>
</network>

View File

@ -0,0 +1,16 @@
/usr/sbin/dnsmasq \
--strict-order \
--bind-interfaces \
--conf-file= \
--except-interface lo \
--srv-host=name.tcp.test-domain-name,.,1024,10,10 \
--listen-address 192.168.122.1 \
--listen-address 192.168.123.1 \
--listen-address 2001:db8:ac10:fe01::1 \
--listen-address 2001:db8:ac10:fd01::1 \
--listen-address 10.24.10.1 \
--dhcp-range 192.168.122.2,192.168.122.254 \
--dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \
--dhcp-lease-max=253 \
--dhcp-no-override \
--dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile\

View File

@ -0,0 +1,26 @@
<network>
<name>default</name>
<uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
<forward dev='eth1' mode='nat'>
<interface dev='eth1'/>
</forward>
<bridge name='virbr0' stp='on' delay='0' />
<dns>
<srv service='name' protocol='tcp' domain='test-domain-name' target='.' port='1024' priority='10' weight='10' />
</dns>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254' />
<host mac='00:16:3e:77:e2:ed' name='a.example.com' ip='192.168.122.10' />
<host mac='00:16:3e:3e:a9:1a' name='b.example.com' ip='192.168.122.11' />
</dhcp>
</ip>
<ip family='ipv4' address='192.168.123.1' netmask='255.255.255.0'>
</ip>
<ip family='ipv6' address='2001:db8:ac10:fe01::1' prefix='64'>
</ip>
<ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
</ip>
<ip family='ipv4' address='10.24.10.1'>
</ip>
</network>

View File

@ -120,6 +120,8 @@ mymain(void)
DO_TEST("netboot-network");
DO_TEST("netboot-proxy-network");
DO_TEST("nat-network-dns-txt-record");
DO_TEST("nat-network-dns-srv-record");
DO_TEST("nat-network-dns-srv-record-minimal");
DO_TEST("nat-network-dns-hosts");
return (ret==0 ? EXIT_SUCCESS : EXIT_FAILURE);

View File

@ -0,0 +1,26 @@
<network>
<name>default</name>
<uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
<forward dev='eth1' mode='nat'>
<interface dev='eth1'/>
</forward>
<bridge name='virbr0' stp='on' delay='0' />
<dns>
<srv service='name' protocol='tcp' />
</dns>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254' />
<host mac='00:16:3e:77:e2:ed' name='a.example.com' ip='192.168.122.10' />
<host mac='00:16:3e:3e:a9:1a' name='b.example.com' ip='192.168.122.11' />
</dhcp>
</ip>
<ip family='ipv4' address='192.168.123.1' netmask='255.255.255.0'>
</ip>
<ip family='ipv6' address='2001:db8:ac10:fe01::1' prefix='64'>
</ip>
<ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
</ip>
<ip family='ipv4' address='10.24.10.1'>
</ip>
</network>

View File

@ -0,0 +1,26 @@
<network>
<name>default</name>
<uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
<forward dev='eth1' mode='nat'>
<interface dev='eth1'/>
</forward>
<bridge name='virbr0' stp='on' delay='0' />
<dns>
<srv service='name' protocol='tcp' domain='test-domain-name' target='.' port='1024' priority='10' weight='10' />
</dns>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254' />
<host mac='00:16:3e:77:e2:ed' name='a.example.com' ip='192.168.122.10' />
<host mac='00:16:3e:3e:a9:1a' name='b.example.com' ip='192.168.122.11' />
</dhcp>
</ip>
<ip family='ipv4' address='192.168.123.1' netmask='255.255.255.0'>
</ip>
<ip family='ipv6' address='2001:db8:ac10:fe01::1' prefix='64'>
</ip>
<ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
</ip>
<ip family='ipv4' address='10.24.10.1'>
</ip>
</network>

View File

@ -0,0 +1,26 @@
<network>
<name>default</name>
<uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
<forward dev='eth1' mode='nat'>
<interface dev='eth1'/>
</forward>
<bridge name='virbr0' stp='on' delay='0' />
<dns>
<srv service='name' protocol='tcp' />
</dns>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254' />
<host mac='00:16:3e:77:e2:ed' name='a.example.com' ip='192.168.122.10' />
<host mac='00:16:3e:3e:a9:1a' name='b.example.com' ip='192.168.122.11' />
</dhcp>
</ip>
<ip family='ipv4' address='192.168.123.1' netmask='255.255.255.0'>
</ip>
<ip family='ipv6' address='2001:db8:ac10:fe01::1' prefix='64'>
</ip>
<ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
</ip>
<ip family='ipv4' address='10.24.10.1'>
</ip>
</network>

View File

@ -0,0 +1,26 @@
<network>
<name>default</name>
<uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
<forward dev='eth1' mode='nat'>
<interface dev='eth1'/>
</forward>
<bridge name='virbr0' stp='on' delay='0' />
<dns>
<srv service='name' protocol='tcp' domain='test-domain-name' target='.' port='1024' priority='10' weight='10' />
</dns>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254' />
<host mac='00:16:3e:77:e2:ed' name='a.example.com' ip='192.168.122.10' />
<host mac='00:16:3e:3e:a9:1a' name='b.example.com' ip='192.168.122.11' />
</dhcp>
</ip>
<ip family='ipv4' address='192.168.123.1' netmask='255.255.255.0'>
</ip>
<ip family='ipv6' address='2001:db8:ac10:fe01::1' prefix='64'>
</ip>
<ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
</ip>
<ip family='ipv4' address='10.24.10.1'>
</ip>
</network>