diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in index 02302fafa0..755d51098b 100644 --- a/docs/formatnetwork.html.in +++ b/docs/formatnetwork.html.in @@ -345,6 +345,7 @@ <domain name="example.com"/> <dns> <txt name="example" value="example value" /> + <srv service='name' protocol='tcp' domain='test-domain-name' target='.' port='1024' priority='10' weight='10'/> <host ip='192.168.122.2'> <hostname>myhost</hostname> <hostname>myhostalias</hostname> @@ -396,6 +397,17 @@ Since 0.9.3 +
+
srv
+
The dns element can have also 0 or more srv + record elements. Each srv 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). + Since 0.9.9 +
+
ip
The address attribute defines an IPv4 address in diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng index 937e1808ee..5d58fe4021 100644 --- a/docs/schemas/network.rng +++ b/docs/schemas/network.rng @@ -137,6 +137,19 @@ + + + + + + + + + + + + + @@ -216,11 +229,4 @@ - - - - (ipv4)|(ipv6) - - - diff --git a/docs/schemas/networkcommon.rng b/docs/schemas/networkcommon.rng index 3a168c337c..2328892f17 100644 --- a/docs/schemas/networkcommon.rng +++ b/docs/schemas/networkcommon.rng @@ -95,4 +95,21 @@ 1 + + + + 0 + 65535 + + + + + (tcp)|(udp) + + + + + (ipv4)|(ipv6) + + diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 1058b0774a..347954374c 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -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, " 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; diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 1be20f8d93..975d5e843c 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -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 # include # include @@ -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; diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 44c80e1852..9afada73bb 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -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); + } + } } /* diff --git a/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv b/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv new file mode 100644 index 0000000000..021e8f07a5 --- /dev/null +++ b/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv @@ -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\ diff --git a/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.xml b/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.xml new file mode 100644 index 0000000000..e9b7680603 --- /dev/null +++ b/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.xml @@ -0,0 +1,26 @@ + + default + 81ff0d90-c91e-6742-64da-4a736edb9a9b + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/networkxml2argvdata/nat-network-dns-srv-record.argv b/tests/networkxml2argvdata/nat-network-dns-srv-record.argv new file mode 100644 index 0000000000..85afbba700 --- /dev/null +++ b/tests/networkxml2argvdata/nat-network-dns-srv-record.argv @@ -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\ diff --git a/tests/networkxml2argvdata/nat-network-dns-srv-record.xml b/tests/networkxml2argvdata/nat-network-dns-srv-record.xml new file mode 100644 index 0000000000..4be85b50a0 --- /dev/null +++ b/tests/networkxml2argvdata/nat-network-dns-srv-record.xml @@ -0,0 +1,26 @@ + + default + 81ff0d90-c91e-6742-64da-4a736edb9a9b + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/networkxml2argvtest.c b/tests/networkxml2argvtest.c index 4a11d6fea1..2dd9b7f14a 100644 --- a/tests/networkxml2argvtest.c +++ b/tests/networkxml2argvtest.c @@ -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); diff --git a/tests/networkxml2xmlin/nat-network-dns-srv-record-minimal.xml b/tests/networkxml2xmlin/nat-network-dns-srv-record-minimal.xml new file mode 100644 index 0000000000..e9b7680603 --- /dev/null +++ b/tests/networkxml2xmlin/nat-network-dns-srv-record-minimal.xml @@ -0,0 +1,26 @@ + + default + 81ff0d90-c91e-6742-64da-4a736edb9a9b + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/networkxml2xmlin/nat-network-dns-srv-record.xml b/tests/networkxml2xmlin/nat-network-dns-srv-record.xml new file mode 100644 index 0000000000..4be85b50a0 --- /dev/null +++ b/tests/networkxml2xmlin/nat-network-dns-srv-record.xml @@ -0,0 +1,26 @@ + + default + 81ff0d90-c91e-6742-64da-4a736edb9a9b + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/networkxml2xmlout/nat-network-dns-srv-record-minimal.xml b/tests/networkxml2xmlout/nat-network-dns-srv-record-minimal.xml new file mode 100644 index 0000000000..e9b7680603 --- /dev/null +++ b/tests/networkxml2xmlout/nat-network-dns-srv-record-minimal.xml @@ -0,0 +1,26 @@ + + default + 81ff0d90-c91e-6742-64da-4a736edb9a9b + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/networkxml2xmlout/nat-network-dns-srv-record.xml b/tests/networkxml2xmlout/nat-network-dns-srv-record.xml new file mode 100644 index 0000000000..4be85b50a0 --- /dev/null +++ b/tests/networkxml2xmlout/nat-network-dns-srv-record.xml @@ -0,0 +1,26 @@ + + default + 81ff0d90-c91e-6742-64da-4a736edb9a9b + + + + + + + + + + + + + + + + + + + + + + +