mirror of
https://github.com/systemd/systemd.git
synced 2024-11-02 19:21:53 +03:00
importd, basic/string-util: use case-insensitive comparison for HTTP headers
According to RFC2616[1], HTTP header names are case-insensitive. So it's totally valid to have a header starting with either `Date:` or `date:`. However, when systemd-importd pulls an image from an HTTP server, it parses HTTP headers by comparing header names as-is, without any conversion. That causes failures when some HTTP servers return headers with different combinations of upper-/lower-cases. An example: https://alpha.release.flatcar-linux.net/amd64-usr/current/flatcar_developer_container.bin.bz2 returns `Etag: "pe89so9oir60"`, while https://alpha.release.core-os.net/amd64-usr/current/coreos_developer_container.bin.bz2 returns `ETag: "f03372edea9a1e7232e282c346099857"`. Since systemd-importd expects to see `ETag`, the etag for the Container Linux image is correctly interpreted as a part of the hidden file name. However, it cannot parse etag for Flatcar Linux, so the etag the Flatcar Linux image is not appended to the hidden file name. ``` $ sudo ls -al /var/lib/machines/ -r--r--r-- 1 root root 3303014400 Aug 21 20:07 '.raw-https:\x2f\x2falpha\x2erelease\x2ecore-os\x2enet\x2famd64-usr\x2fcurrent\x2fcoreos_developer_container\x2ebin\x2ebz2.\x22f03372edea9a1e7232e282c346099857\x22.raw' -r--r--r-- 1 root root 3303014400 Aug 17 06:15 '.raw-https:\x2f\x2falpha\x2erelease\x2eflatcar-linux\x2enet\x2famd64-usr\x2fcurrent\x2fflatcar_developer_container\x2ebin\x2ebz2.raw' ``` As a result, when the Flatcar image is removed and downloaded again, systemd-importd is not able to determine if the file has been already downloaded, so it always download it again. Then it fails to rename it to an expected name, because there's already a hidden file. To fix this issue, let's introduce a new helper function `memory_startswith_no_case()`, which compares memory regions in a case-insensitive way. Use this function in `curl_header_strdup()`. See also https://github.com/kinvolk/kube-spawn/issues/304 [1]: https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
This commit is contained in:
parent
8b247b43c8
commit
21224070e8
@ -228,3 +228,25 @@ static inline void *memory_startswith(const void *p, size_t sz, const char *toke
|
||||
|
||||
return (uint8_t*) p + n;
|
||||
}
|
||||
|
||||
/* Like startswith_no_case(), but operates on arbitrary memory blocks.
|
||||
* It works only for ASCII strings.
|
||||
*/
|
||||
static inline void *memory_startswith_no_case(const void *p, size_t sz, const char *token) {
|
||||
size_t n, i;
|
||||
|
||||
assert(token);
|
||||
|
||||
n = strlen(token);
|
||||
if (sz < n)
|
||||
return NULL;
|
||||
|
||||
assert(p);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (ascii_tolower(((char *)p)[i]) != ascii_tolower(token[i]))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (uint8_t*) p + n;
|
||||
}
|
||||
|
@ -363,7 +363,7 @@ int curl_header_strdup(const void *contents, size_t sz, const char *field, char
|
||||
const char *p;
|
||||
char *s;
|
||||
|
||||
p = memory_startswith(contents, sz, field);
|
||||
p = memory_startswith_no_case(contents, sz, field);
|
||||
if (!p)
|
||||
return 0;
|
||||
|
||||
|
@ -496,6 +496,29 @@ static void test_memory_startswith(void) {
|
||||
assert_se(!memory_startswith("xxx", 4, "xxxx"));
|
||||
}
|
||||
|
||||
static void test_memory_startswith_no_case(void) {
|
||||
assert_se(streq(memory_startswith_no_case("", 0, ""), ""));
|
||||
assert_se(streq(memory_startswith_no_case("", 1, ""), ""));
|
||||
assert_se(streq(memory_startswith_no_case("x", 2, ""), "x"));
|
||||
assert_se(streq(memory_startswith_no_case("X", 2, ""), "X"));
|
||||
assert_se(!memory_startswith_no_case("", 1, "X"));
|
||||
assert_se(!memory_startswith_no_case("", 1, "xxxxXXXX"));
|
||||
assert_se(streq(memory_startswith_no_case("xxx", 4, "X"), "xx"));
|
||||
assert_se(streq(memory_startswith_no_case("XXX", 4, "x"), "XX"));
|
||||
assert_se(streq(memory_startswith_no_case("XXX", 4, "X"), "XX"));
|
||||
assert_se(streq(memory_startswith_no_case("xxx", 4, "XX"), "x"));
|
||||
assert_se(streq(memory_startswith_no_case("XXX", 4, "xx"), "X"));
|
||||
assert_se(streq(memory_startswith_no_case("XXX", 4, "XX"), "X"));
|
||||
assert_se(streq(memory_startswith_no_case("xxx", 4, "XXX"), ""));
|
||||
assert_se(streq(memory_startswith_no_case("XXX", 4, "xxx"), ""));
|
||||
assert_se(streq(memory_startswith_no_case("XXX", 4, "XXX"), ""));
|
||||
|
||||
assert_se(memory_startswith_no_case((char[2]){'x', 'x'}, 2, "xx"));
|
||||
assert_se(memory_startswith_no_case((char[2]){'x', 'X'}, 2, "xX"));
|
||||
assert_se(memory_startswith_no_case((char[2]){'X', 'x'}, 2, "Xx"));
|
||||
assert_se(memory_startswith_no_case((char[2]){'X', 'X'}, 2, "XX"));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
test_string_erase();
|
||||
test_ascii_strcasecmp_n();
|
||||
@ -525,6 +548,7 @@ int main(int argc, char *argv[]) {
|
||||
test_first_word();
|
||||
test_strlen_ptr();
|
||||
test_memory_startswith();
|
||||
test_memory_startswith_no_case();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user