Add possibility to specify one or more cookies for http based disks.
This patch adds the config parser, storage and validation of the
cookies.
---
docs/formatdomain.html.in | 9 ++
docs/schemas/domaincommon.rng | 39 +++++--
src/conf/domain_conf.c | 108 +++++++++++++++++-
src/util/virstoragefile.c | 124 +++++++++++++++++++++
src/util/virstoragefile.h | 14 +++
.../generic-disk-network-http.xml | 4 +
6 files changed, 289 insertions(+), 9 deletions(-)
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index e31a271a5..ab70edff3 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -2245,6 +2245,9 @@
<driver name='qemu' type='raw'/>
<source protocol="http" name="url_path">
<host name="hostname" port="80"/>
+ <cookies>
+ <cookie name="test">somevalue</cookie>
+ </cookies>
</source>
<target dev='hde' bus='ide' tray='open'/>
<readonly/>
@@ -2593,6 +2596,12 @@
protocol. Supported for 'rbd' <span
class="since">since 1.2.11
(QEMU only).</span>
</dd>
+ <dt><code>cookies</code></dt>
+ <dd>
+ For <code>http</code> and <code>https</code> accessed
storage it's
+ possible to pass one or more cookies. The cookie name and value
+ must conform to the HTTP specification.
+ </dd>
</dl>
<p>
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 0c51f5151..b2fa72381 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -1556,16 +1556,39 @@
</element>
</define>
+ <define name="diskSourceNetworkProtocolHTTPCookies">
+ <element name="cookies">
+ <oneOrMore>
+ <element name="cookie">
+ <attribute name="name">
+ <data type="string">
+ <param
name="pattern">[!#$%&'*+\-.0-9A-Z\^_`a-z|~]+</param>
+ </data>
+ </attribute>
+ <data type="string">
+ <param
name="pattern">[!#$%&'()*+\-./0-9:>=<?@A-Z\^_`\[\]a-z|~]+</param>
+ </data>
+ </element>
+ </oneOrMore>
+ <empty/>
+ </element>
+ </define>
+
<define name="diskSourceNetworkProtocolHTTP">
<element name="source">
- <attribute name="protocol">
- <choice>
- <value>http</value>
- <value>https</value>
- </choice>
- </attribute>
- <attribute name="name"/>
- <ref name="diskSourceNetworkHost"/>
+ <interleave>
+ <attribute name="protocol">
+ <choice>
+ <value>http</value>
+ <value>https</value>
+ </choice>
+ </attribute>
+ <attribute name="name"/>
+ <ref name="diskSourceNetworkHost"/>
+ <optional>
+ <ref name="diskSourceNetworkProtocolHTTPCookies"/>
+ </optional>
+ </interleave>
</element>
</define>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 5a736c853..b1a357174 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -7520,6 +7520,78 @@ virDomainDiskSourcePoolDefParse(xmlNodePtr node,
}
+static virStorageNetCookieDefPtr
+virDomainStorageCookieParse(xmlNodePtr node,
+ xmlXPathContextPtr ctxt)
+{
+ virStorageNetCookieDefPtr cookie = NULL;
+ virStorageNetCookieDefPtr ret = NULL;
+ xmlNodePtr oldnode = ctxt->node;
+
+ ctxt->node = node;
+
+ if (VIR_ALLOC(cookie) < 0)
+ goto cleanup;
+
+ if (!(cookie->name = virXPathString("string(./@name)", ctxt))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s", _("missing cookie
name"));
+ goto cleanup;
+ }
+
+ if (!(cookie->value = virXPathString("string(.)", ctxt))) {
+ virReportError(VIR_ERR_XML_ERROR, _("missing value for cookie
'%s'"),
+ cookie->name);
+ goto cleanup;
+ }
+
+ VIR_STEAL_PTR(ret, cookie);
+
+ cleanup:
+ ctxt->node = oldnode;
+ virStorageNetCookieDefFree(cookie);
+ return ret;
+}
+
+
+static int
+virDomainStorageCookiesParse(xmlNodePtr node,
+ xmlXPathContextPtr ctxt,
+ virStorageSourcePtr src)
+{
+ xmlNodePtr oldnode = ctxt->node;
+ xmlNodePtr *nodes = NULL;
+ ssize_t nnodes;
+ size_t i;
+ int ret = -1;
+
+ ctxt->node = node;
+
+ if ((nnodes = virXPathNodeSet("./cookie", ctxt, &nodes)) < 0)
+ goto cleanup;
+
+ if (VIR_ALLOC_N(src->cookies, nnodes) < 0)
+ goto cleanup;
+
+ src->ncookies = nnodes;
+
+ for (i = 0; i < nnodes; i++) {
+ if (!(src->cookies[i] = virDomainStorageCookieParse(nodes[i], ctxt)))
+ goto cleanup;
+ }
+
+ if (virStorageSourceNetCookiesValidate(src) < 0)
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ VIR_FREE(nodes);
+ ctxt->node = oldnode;
+
+ return ret;
+}
+
+
int
virDomainDiskSourceParse(xmlNodePtr node,
xmlXPathContextPtr ctxt,
@@ -7528,6 +7600,7 @@ virDomainDiskSourceParse(xmlNodePtr node,
int ret = -1;
char *protocol = NULL;
xmlNodePtr saveNode = ctxt->node;
+ xmlNodePtr tmpnode;
ctxt->node = node;
@@ -7590,6 +7663,12 @@ virDomainDiskSourceParse(xmlNodePtr node,
if (virDomainStorageHostParse(node, &src->hosts, &src->nhosts) <
0)
goto cleanup;
+
+ if (src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTP &&
+ (tmpnode = virXPathNode("./cookies", ctxt))) {
+ if (virDomainStorageCookiesParse(tmpnode, ctxt, src) < 0)
+ goto cleanup;
+ }
break;
case VIR_STORAGE_TYPE_VOLUME:
if (virDomainDiskSourcePoolDefParse(node, &src->srcpool) < 0)
@@ -20769,6 +20848,30 @@ virDomainSourceDefFormatSeclabel(virBufferPtr buf,
static int
+virDomainDiskSourceFormatNetworkCookies(virBufferPtr buf,
+ virStorageSourcePtr src)
+{
+ size_t i;
+
+ if (src->ncookies == 0)
+ return 0;
+
+ virBufferAddLit(buf, "<cookies>\n");
+ virBufferAdjustIndent(buf, 2);
+
+ for (i = 0; i < src->ncookies; i++) {
+ virBufferEscapeString(buf, "<cookie name='%s'>",
src->cookies[i]->name);
+ virBufferEscapeString(buf, "%s</cookie>\n",
src->cookies[i]->value);
+ }
+
+ virBufferAdjustIndent(buf, -2);
+ virBufferAddLit(buf, "</cookies>\n");
+
+ return 0;
+}
+
+
+static int
virDomainDiskSourceFormatNetwork(virBufferPtr buf,
virStorageSourcePtr src)
{
@@ -20787,7 +20890,7 @@ virDomainDiskSourceFormatNetwork(virBufferPtr buf,
VIR_FREE(path);
- if (src->nhosts == 0 && !src->snapshot && !src->configFile)
{
+ if (src->nhosts == 0 && !src->snapshot && !src->configFile
&& src->ncookies == 0) {
virBufferAddLit(buf, "/>\n");
} else {
virBufferAddLit(buf, ">\n");
@@ -20809,6 +20912,9 @@ virDomainDiskSourceFormatNetwork(virBufferPtr buf,
virBufferEscapeString(buf, "<snapshot name='%s'/>\n",
src->snapshot);
virBufferEscapeString(buf, "<config file='%s'/>\n",
src->configFile);
+ if (virDomainDiskSourceFormatNetworkCookies(buf, src) < 0)
+ return -1;
+
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "</source>\n");
}
diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c
index 94a77ce86..fe03a3009 100644
--- a/src/util/virstoragefile.c
+++ b/src/util/virstoragefile.c
@@ -1948,6 +1948,126 @@ virStorageSourceSeclabelsCopy(virStorageSourcePtr to,
}
+void
+virStorageNetCookieDefFree(virStorageNetCookieDefPtr def)
+{
+ if (!def)
+ return;
+
+ VIR_FREE(def->name);
+ VIR_FREE(def->value);
+
+ VIR_FREE(def);
+}
+
+
+static void
+virStorageSourceCookiesClear(virStorageSourcePtr src)
+{
+ size_t i;
+
+ if (!src || !src->cookies)
+ return;
+
+ for (i = 0; i < src->ncookies; i++)
+ virStorageNetCookieDefFree(src->cookies[i]);
+
+ VIR_FREE(src->cookies);
+ src->ncookies = 0;
+}
+
+
+static int
+virStorageSourceNetCookiesCopy(virStorageSourcePtr to,
+ const virStorageSource *from)
+{
+ size_t i;
+
+ if (from->ncookies == 0)
+ return 0;
+
+ if (VIR_ALLOC_N(to->cookies, from->ncookies) < 0)
+ return -1;
+ to->ncookies = from->ncookies;
+
+ for (i = 0; i < from->ncookies; i++) {
+ if (VIR_STRDUP(to->cookies[i]->name, from->cookies[i]->name) < 0
||
+ VIR_STRDUP(to->cookies[i]->value, from->cookies[i]->value) <
0)
+ goto error;
+ }
+
+ return 0;
+
+ error:
+ virStorageSourceCookiesClear(to);
+ return -1;
+}
+
+
+/* see
https://tools.ietf.org/html/rfc6265#section-4.1.1 */
+static const char virStorageSourceCookieValueInvalidChars[] =
+ "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
+ " \",;\\";
+
+/* in addition cookie name can't contain these */
+static const char virStorageSourceCookieNameInvalidChars[] =
+ "()<>@:/[]?={}";
+
+static int
+virStorageSourceNetCookieValidate(virStorageNetCookieDefPtr def)
+{
+ /* name must have at least 1 character */
+ if (*(def->name) == '\0') {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("cookie name must not be empty"));
+ return -1;
+ }
+
+ /* check invalid characters in name */
+ if (virStringHasChars(def->name, virStorageSourceCookieValueInvalidChars) ||
+ virStringHasChars(def->name, virStorageSourceCookieNameInvalidChars)) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("cookie name '%s' contains invalid
characters"),
+ def->name);
+ return -1;
+ }
+
+ /* check invalid characters in value */
+ if (virStringHasChars(def->value, virStorageSourceCookieValueInvalidChars)) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("value of cookie '%s' contains invalid
characters"),
+ def->name);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int
+virStorageSourceNetCookiesValidate(virStorageSourcePtr src)
+{
+ size_t i;
+ size_t j;
+
+ for (i = 0; i < src->ncookies; i++) {
+ if (virStorageSourceNetCookieValidate(src->cookies[i]) < 0)
+ return -1;
+
+ for (j = i + 1; j < src->ncookies; j++) {
+ if (STREQ(src->cookies[i]->name, src->cookies[j]->name)) {
+ virReportError(VIR_ERR_XML_ERROR, _("duplicate cookie
'%s'"),
+ src->cookies[i]->name);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
static virStorageTimestampsPtr
virStorageTimestampsCopy(const virStorageTimestamps *src)
{
@@ -2060,6 +2180,9 @@ virStorageSourceCopy(const virStorageSource *src,
ret->nhosts = src->nhosts;
}
+ if (virStorageSourceNetCookiesCopy(ret, src) < 0)
+ goto error;
+
if (src->srcpool &&
!(ret->srcpool = virStorageSourcePoolDefCopy(src->srcpool)))
goto error;
@@ -2258,6 +2381,7 @@ virStorageSourceClear(virStorageSourcePtr def)
VIR_FREE(def->volume);
VIR_FREE(def->snapshot);
VIR_FREE(def->configFile);
+ virStorageSourceCookiesClear(def);
virStorageSourcePoolDefFree(def->srcpool);
VIR_FREE(def->driverName);
virBitmapFree(def->features);
diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h
index 9ebfc1108..42d9eac61 100644
--- a/src/util/virstoragefile.h
+++ b/src/util/virstoragefile.h
@@ -160,6 +160,16 @@ struct _virStorageNetHostDef {
char *socket; /* path to unix socket */
};
+typedef struct _virStorageNetCookieDef virStorageNetCookieDef;
+typedef virStorageNetCookieDef *virStorageNetCookieDefPtr;
+struct _virStorageNetCookieDef {
+ char *name;
+ char *value;
+};
+
+void virStorageNetCookieDefFree(virStorageNetCookieDefPtr def);
+
+
/* Information for a storage volume from a virStoragePool */
/*
@@ -235,6 +245,8 @@ struct _virStorageSource {
the source definition */
size_t nhosts;
virStorageNetHostDefPtr hosts;
+ size_t ncookies;
+ virStorageNetCookieDefPtr *cookies;
virStorageSourcePoolDefPtr srcpool;
virStorageAuthDefPtr auth;
virStorageEncryptionPtr encryption;
@@ -371,6 +383,8 @@ int virStorageSourceUpdateCapacity(virStorageSourcePtr src,
char *buf, ssize_t len,
bool probe);
+int virStorageSourceNetCookiesValidate(virStorageSourcePtr src);
+
virStorageSourcePtr virStorageSourceNewFromBacking(virStorageSourcePtr parent);
virStorageSourcePtr virStorageSourceCopy(const virStorageSource *src,
bool backingChain)
diff --git a/tests/genericxml2xmlindata/generic-disk-network-http.xml
b/tests/genericxml2xmlindata/generic-disk-network-http.xml
index 51c779502..fc5ac6e5e 100644
--- a/tests/genericxml2xmlindata/generic-disk-network-http.xml
+++ b/tests/genericxml2xmlindata/generic-disk-network-http.xml
@@ -32,6 +32,10 @@
<driver name='qemu' type='raw'/>
<source protocol='http' name='test3.img'>
<host name='example.org' port='1234'/>
+ <cookies>
+ <cookie name='test'>testcookievalue</cookie>
+ <cookie name='test2'>blurb</cookie>
+ </cookies>
</source>
<target dev='vdc' bus='virtio'/>
</disk>
--
2.12.2