Add TXT record support for virtual DNS service

This commit introduces the <dns> element and <txt> record for the
virtual DNS network. The DNS TXT record can be defined using following
syntax in the network XML file:

  <dns>
    <txt name="example" value="example value" />
  </dns>

Also, the Relax-NG scheme has been altered to allow the texts without
spaces only for the name element and some nitpicks about memory
free'ing have been fixed by Laine so therefore I'm adding Laine to the
SOB clause ;-)

Signed-off-by: Michal Novotny <minovotn@redhat.com>
Signed-off-by: Laine Stump <laine@laine.org>
This commit is contained in:
Michal Novotny 2011-06-24 12:04:36 +02:00 committed by Laine Stump
parent 9a48ed54f8
commit 5dd986dbd7
8 changed files with 237 additions and 1 deletions

View File

@ -114,6 +114,9 @@
<pre>
...
&lt;mac address='00:16:3E:5D:C7:9E'/&gt;
&lt;dns&gt;
&lt;txt name="example" value="example value" /&gt;
&lt;/dns&gt;
&lt;ip address="192.168.122.1" netmask="255.255.255.0"&gt;
&lt;dhcp&gt;
&lt;range start="192.168.122.100" end="192.168.122.254" /&gt;
@ -166,7 +169,25 @@
supported for IPv6 addresses, can only be specified on a single IPv4 address
per network.
<span class="since">Since 0.7.1</span>
</dd><dt><code>dhcp</code></dt><dd>Also within the <code>ip</code> element there is an
</dd>
<dt><code>dns</code></dt><dd>
The dns element of a network contains configuration information for the
virtual network's DNS server. <span class="since">Since 0.9.3</span>
Currently supported elements are:
<dl>
<dt><code>txt</code></dt>
<dd>A <code>dns</code> element can have 0 or more <code>txt</code> elements.
Each txt element defines a DNS TXT record and has two attributes, both
required: a name that can be queried via dns, and a value that will be
returned when that name is queried. names cannot contain embedded spaces
or commas. value is a single string that can contain multiple values
separated by commas. <span class="since">Since 0.9.3</span>
</dd>
</dl>
</dd>
<dt><code>dhcp</code></dt>
<dd>Also within the <code>ip</code> element there is an
optional <code>dhcp</code> element. The presence of this element
enables DHCP services on the virtual network. It will further
contain one or more <code>range</code> elements. The

View File

@ -87,6 +87,19 @@
</element>
</optional>
<!-- Define the DNS related elements like TXT records
and other features in the <dns> element -->
<optional>
<element name="dns">
<zeroOrMore>
<element name="txt">
<attribute name="name"><ref name="dns-name"/></attribute>
<attribute name="value"><text/></attribute>
</element>
</zeroOrMore>
</element>
</optional>
<!-- <ip> element -->
<zeroOrMore>
<!-- The IP element sets up NAT'ing and an optional DHCP server
@ -183,4 +196,11 @@
</data>
</define>
<!-- a valid DNS name -->
<define name='dns-name'>
<data type='string'>
<param name="pattern">([a-zA-Z\-]+)</param>
</data>
</define>
</grammar>

View File

@ -104,6 +104,20 @@ static void virNetworkIpDefClear(virNetworkIpDefPtr def)
VIR_FREE(def->bootfile);
}
static void virNetworkDNSDefFree(virNetworkDNSDefPtr def)
{
if (def) {
if (def->txtrecords) {
while (def->ntxtrecords--) {
VIR_FREE(def->txtrecords[def->ntxtrecords].name);
VIR_FREE(def->txtrecords[def->ntxtrecords].value);
}
VIR_FREE(def->txtrecords);
}
VIR_FREE(def);
}
}
void virNetworkDefFree(virNetworkDefPtr def)
{
int ii;
@ -121,6 +135,8 @@ void virNetworkDefFree(virNetworkDefPtr def)
}
VIR_FREE(def->ips);
virNetworkDNSDefFree(def->dns);
VIR_FREE(def);
}
@ -434,6 +450,69 @@ virNetworkDHCPRangeDefParseXML(const char *networkName,
return 0;
}
static int
virNetworkDNSDefParseXML(virNetworkDNSDefPtr *dnsdef,
xmlNodePtr node)
{
xmlNodePtr cur;
int ret = -1;
char *name = NULL;
char *value = NULL;
virNetworkDNSDefPtr def = NULL;
if (VIR_ALLOC(def) < 0) {
virReportOOMError();
goto error;
}
cur = node->children;
while (cur != NULL) {
if (cur->type == XML_ELEMENT_NODE &&
xmlStrEqual(cur->name, BAD_CAST "txt")) {
if (!(name = virXMLPropString(cur, "name"))) {
virNetworkReportError(VIR_ERR_XML_DETAIL,
"%s", _("Missing required name attribute in dns txt record"));
goto error;
}
if (!(value = virXMLPropString(cur, "value"))) {
virNetworkReportError(VIR_ERR_XML_DETAIL,
_("Missing required value attribute in dns txt record '%s'"), name);
goto error;
}
if (strchr(name, ' ') != NULL) {
virNetworkReportError(VIR_ERR_XML_DETAIL,
_("spaces are not allowed in DNS TXT record names (name is '%s')"), name);
goto error;
}
if (VIR_REALLOC_N(def->txtrecords, def->ntxtrecords + 1) < 0) {
virReportOOMError();
goto error;
}
def->txtrecords[def->ntxtrecords].name = name;
def->txtrecords[def->ntxtrecords].value = value;
def->ntxtrecords++;
name = NULL;
value = NULL;
}
cur = cur->next;
}
ret = 0;
error:
if (ret < 0) {
VIR_FREE(name);
VIR_FREE(value);
virNetworkDNSDefFree(def);
} else {
*dnsdef = def;
}
return ret;
}
static int
virNetworkIPParseXML(const char *networkName,
virNetworkIpDefPtr def,
@ -584,6 +663,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
virNetworkDefPtr def;
char *tmp;
xmlNodePtr *ipNodes = NULL;
xmlNodePtr dnsNode = NULL;
int nIps;
if (VIR_ALLOC(def) < 0) {
@ -641,6 +721,12 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
def->mac_specified = true;
}
dnsNode = virXPathNode("./dns", ctxt);
if (dnsNode != NULL) {
if (virNetworkDNSDefParseXML(&def->dns, dnsNode) < 0)
goto error;
}
nIps = virXPathNodeSet("./ip", ctxt, &ipNodes);
if (nIps < 0)
goto error;
@ -750,6 +836,29 @@ cleanup:
return def;
}
static int
virNetworkDNSDefFormat(virBufferPtr buf,
virNetworkDNSDefPtr def)
{
int result = 0;
int i;
if (def == NULL)
goto out;
virBufferAddLit(buf, " <dns>\n");
for (i = 0 ; i < def->ntxtrecords ; i++) {
virBufferAsprintf(buf, " <txt name='%s' value='%s' />\n",
def->txtrecords[i].name,
def->txtrecords[i].value);
}
virBufferAddLit(buf, " </dns>\n");
out:
return result;
}
static int
virNetworkIpDefFormat(virBufferPtr buf,
const virNetworkIpDefPtr def)
@ -881,6 +990,9 @@ char *virNetworkDefFormat(const virNetworkDefPtr def)
if (def->domain)
virBufferAsprintf(&buf, " <domain name='%s'/>\n", def->domain);
if (virNetworkDNSDefFormat(&buf, def->dns) < 0)
goto error;
for (ii = 0; ii < def->nips; ii++) {
if (virNetworkIpDefFormat(&buf, &def->ips[ii]) < 0)
goto error;

View File

@ -57,6 +57,20 @@ struct _virNetworkDHCPHostDef {
virSocketAddr ip;
};
typedef struct _virNetworkDNSTxtRecordsDef virNetworkDNSTxtRecordsDef;
typedef virNetworkDNSTxtRecordsDef *virNetworkDNSTxtRecordsDefPtr;
struct _virNetworkDNSTxtRecordsDef {
char *name;
char *value;
};
struct virNetworkDNSDef {
unsigned int ntxtrecords;
virNetworkDNSTxtRecordsDefPtr txtrecords;
} virNetworkDNSDef;
typedef struct virNetworkDNSDef *virNetworkDNSDefPtr;
typedef struct _virNetworkIpDef virNetworkIpDef;
typedef virNetworkIpDef *virNetworkIpDefPtr;
struct _virNetworkIpDef {
@ -101,6 +115,8 @@ struct _virNetworkDef {
size_t nips;
virNetworkIpDefPtr ips; /* ptr to array of IP addresses on this network */
virNetworkDNSDefPtr dns; /* ptr to dns related configuration */
};
typedef struct _virNetworkObj virNetworkObj;

View File

@ -510,6 +510,24 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
if (network->def->forwardType == VIR_NETWORK_FORWARD_NONE)
virCommandAddArg(cmd, "--dhcp-option=3");
if (network->def->dns != NULL) {
virNetworkDNSDefPtr dns = network->def->dns;
int i;
for (i = 0; i < dns->ntxtrecords; i++) {
char *record = NULL;
if (virAsprintf(&record, "%s,%s",
dns->txtrecords[i].name,
dns->txtrecords[i].value) < 0) {
virReportOOMError();
goto cleanup;
}
virCommandAddArgPair(cmd, "--txt-record", record);
VIR_FREE(record);
}
}
/*
* --interface does not actually work with dnsmasq < 2.47,
* due to DAD for ipv6 addresses on the interface.

View File

@ -0,0 +1,24 @@
<network>
<name>default</name>
<uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
<forward dev='eth1' mode='nat'/>
<bridge name='virbr0' stp='on' delay='0' />
<dns>
<txt name='example' value='example value' />
</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,24 @@
<network>
<name>default</name>
<uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
<forward dev='eth1' mode='nat'/>
<bridge name='virbr0' stp='on' delay='0' />
<dns>
<txt name='example' value='example value' />
</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

@ -86,6 +86,7 @@ mymain(void)
DO_TEST("nat-network");
DO_TEST("netboot-network");
DO_TEST("netboot-proxy-network");
DO_TEST("nat-network-dns-txt-record");
return (ret==0 ? EXIT_SUCCESS : EXIT_FAILURE);
}