diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c
index d144fb817a..edd0ebe470 100644
--- a/lib/http_aws_sigv4.c
+++ b/lib/http_aws_sigv4.c
@@ -44,6 +44,8 @@
#include "curl_memory.h"
#include "memdebug.h"
+#include "slist.h"
+
#define HMAC_SHA256(k, kl, d, dl, o) \
do { \
ret = Curl_hmacit(Curl_HMAC_SHA256, \
@@ -51,49 +53,240 @@
(unsigned int)kl, \
(unsigned char *)d, \
(unsigned int)dl, o); \
- if(ret != CURLE_OK) { \
+ if(ret) { \
goto fail; \
} \
} while(0)
+#define TIMESTAMP_SIZE 17
+
static void sha256_to_hex(char *dst, unsigned char *sha, size_t dst_l)
{
int i;
DEBUGASSERT(dst_l >= 65);
for(i = 0; i < 32; ++i) {
- curl_msnprintf(dst + (i * 2), dst_l - (i * 2), "%02x", sha[i]);
+ msnprintf(dst + (i * 2), dst_l - (i * 2), "%02x", sha[i]);
}
}
+static char *find_date_hdr(struct Curl_easy *data, const char *sig_hdr)
+{
+ char *tmp = Curl_checkheaders(data, sig_hdr, strlen(sig_hdr));
+
+ if(tmp)
+ return tmp;
+ return Curl_checkheaders(data, STRCONST("Date"));
+}
+
+/* remove whitespace, and lowercase all headers */
+static void trim_headers(struct curl_slist *head)
+{
+ struct curl_slist *l;
+ for(l = head; l; l = l->next) {
+ char *value; /* to read from */
+ char *store;
+ size_t colon = strcspn(l->data, ":");
+ Curl_strntolower(l->data, l->data, colon);
+
+ value = &l->data[colon];
+ if(!*value)
+ continue;
+ ++value;
+ store = value;
+
+ /* skip leading whitespace */
+ while(*value && ISBLANK(*value))
+ value++;
+
+ while(*value) {
+ int space = 0;
+ while(*value && ISBLANK(*value)) {
+ value++;
+ space++;
+ }
+ if(space) {
+ /* replace any number of consecutive whitespace with a single space,
+ unless at the end of the string, then nothing */
+ if(*value)
+ *store++ = ' ';
+ }
+ else
+ *store++ = *value++;
+ }
+ *store = 0; /* null terminate */
+ }
+}
+
+/* maximum lenth for the aws sivg4 parts */
+#define MAX_SIGV4_LEN 64
+#define MAX_SIGV4_LEN_TXT "64"
+
+#define DATE_HDR_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Date"))
+
+/* FQDN + host: */
+#define FULL_HOST_LEN (255 + sizeof("host:"))
+
+/* string been x-PROVIDER-date:TIMESTAMP, I need +1 for ':' */
+#define DATE_FULL_HDR_LEN (DATE_HDR_KEY_LEN + TIMESTAMP_SIZE + 1)
+
+/* timestamp should point to a buffer of at last TIMESTAMP_SIZE bytes */
+static CURLcode make_headers(struct Curl_easy *data,
+ const char *hostname,
+ char *timestamp,
+ char *provider1,
+ char **date_header,
+ struct dynbuf *canonical_headers,
+ struct dynbuf *signed_headers)
+{
+ char date_hdr_key[DATE_HDR_KEY_LEN];
+ char date_full_hdr[DATE_FULL_HDR_LEN];
+ struct curl_slist *head = NULL;
+ struct curl_slist *tmp_head = NULL;
+ CURLcode ret = CURLE_OUT_OF_MEMORY;
+ struct curl_slist *l;
+ int again = 1;
+
+ /* provider1 mid */
+ Curl_strntolower(provider1, provider1, strlen(provider1));
+ provider1[0] = Curl_raw_toupper(provider1[0]);
+
+ msnprintf(date_hdr_key, DATE_HDR_KEY_LEN, "X-%s-Date", provider1);
+
+ /* provider1 lowercase */
+ Curl_strntolower(provider1, provider1, 1); /* first byte only */
+ msnprintf(date_full_hdr, DATE_FULL_HDR_LEN,
+ "x-%s-date:%s", provider1, timestamp);
+
+ if(Curl_checkheaders(data, STRCONST("Host"))) {
+ head = NULL;
+ }
+ else {
+ char full_host[FULL_HOST_LEN];
+
+ if(data->state.aptr.host) {
+ size_t pos;
+
+ if(strlen(data->state.aptr.host) > FULL_HOST_LEN) {
+ ret = CURLE_URL_MALFORMAT;
+ goto fail;
+ }
+ strcpy(full_host, data->state.aptr.host);
+ /* remove /r/n as the separator for canonical request must be '\n' */
+ pos = strcspn(full_host, "\n\r");
+ full_host[pos] = 0;
+ }
+ else {
+ if(strlen(hostname) > FULL_HOST_LEN) {
+ ret = CURLE_URL_MALFORMAT;
+ goto fail;
+ }
+ msnprintf(full_host, FULL_HOST_LEN, "host:%s", hostname);
+ }
+
+ head = curl_slist_append(NULL, full_host);
+ if(!head)
+ goto fail;
+ }
+
+
+ for(l = data->set.headers; l; l = l->next) {
+ tmp_head = curl_slist_append(head, l->data);
+ if(!tmp_head)
+ goto fail;
+ head = tmp_head;
+ }
+
+ trim_headers(head);
+
+ *date_header = find_date_hdr(data, date_hdr_key);
+ if(!*date_header) {
+ tmp_head = curl_slist_append(head, date_full_hdr);
+ if(!tmp_head)
+ goto fail;
+ head = tmp_head;
+ *date_header = curl_maprintf("%s: %s", date_hdr_key, timestamp);
+ }
+ else {
+ char *value;
+
+ *date_header = strdup(*date_header);
+ if(!*date_header)
+ goto fail;
+
+ value = strchr(*date_header, ':');
+ if(!value)
+ goto fail;
+ ++value;
+ while(ISBLANK(*value))
+ ++value;
+ strncpy(timestamp, value, TIMESTAMP_SIZE - 1);
+ timestamp[TIMESTAMP_SIZE - 1] = 0;
+ }
+
+ /* alpha-sort in a case sensitive manner */
+ do {
+ again = 0;
+ for(l = head; l; l = l->next) {
+ struct curl_slist *next = l->next;
+
+ if(next && strcmp(l->data, next->data) > 0) {
+ char *tmp = l->data;
+
+ l->data = next->data;
+ next->data = tmp;
+ again = 1;
+ }
+ }
+ } while(again);
+
+ for(l = head; l; l = l->next) {
+ char *tmp;
+
+ if(Curl_dyn_add(canonical_headers, l->data))
+ goto fail;
+ if(Curl_dyn_add(canonical_headers, "\n"))
+ goto fail;
+
+ tmp = strchr(l->data, ':');
+ if(tmp)
+ *tmp = 0;
+
+ if(l != head) {
+ if(Curl_dyn_add(signed_headers, ";"))
+ goto fail;
+ }
+ if(Curl_dyn_add(signed_headers, l->data))
+ goto fail;
+ }
+
+ ret = CURLE_OK;
+fail:
+ curl_slist_free_all(head);
+
+ return ret;
+}
+
CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
{
CURLcode ret = CURLE_OUT_OF_MEMORY;
struct connectdata *conn = data->conn;
size_t len;
- const char *tmp0;
- const char *tmp1;
- char *provider0_low = NULL;
- char *provider0_up = NULL;
- char *provider1_low = NULL;
- char *provider1_mid = NULL;
- char *region = NULL;
- char *service = NULL;
+ const char *arg;
+ char provider0[MAX_SIGV4_LEN + 1]="";
+ char provider1[MAX_SIGV4_LEN + 1]="";
+ char region[MAX_SIGV4_LEN + 1]="";
+ char service[MAX_SIGV4_LEN + 1]="";
const char *hostname = conn->host.name;
-#ifdef DEBUGBUILD
- char *force_timestamp;
-#endif
time_t clock;
struct tm tm;
- char timestamp[17];
+ char timestamp[TIMESTAMP_SIZE];
char date[9];
- const char *content_type = Curl_checkheaders(data, STRCONST("Content-Type"));
- char *canonical_headers = NULL;
- char *signed_headers = NULL;
- Curl_HttpReq httpreq;
- const char *method;
- size_t post_data_len;
- const char *post_data = data->set.postfields ? data->set.postfields : "";
+ struct dynbuf canonical_headers;
+ struct dynbuf signed_headers;
+ char *date_header = NULL;
+ const char *post_data = data->set.postfields;
+ size_t post_data_len = 0;
unsigned char sha_hash[32];
char sha_hex[65];
char *canonical_request = NULL;
@@ -101,10 +294,9 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
char *credential_scope = NULL;
char *str_to_sign = NULL;
const char *user = data->state.aptr.user ? data->state.aptr.user : "";
- const char *passwd = data->state.aptr.passwd ? data->state.aptr.passwd : "";
char *secret = NULL;
- unsigned char tmp_sign0[32] = {0};
- unsigned char tmp_sign1[32] = {0};
+ unsigned char sign0[32] = {0};
+ unsigned char sign1[32] = {0};
char *auth_headers = NULL;
DEBUGASSERT(!proxy);
@@ -115,6 +307,10 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
return CURLE_OK;
}
+ /* we init thoses buffers here, so goto fail will free initialized dynbuf */
+ Curl_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER);
+ Curl_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER);
+
/*
* Parameters parsing
* Google and Outscale use the same OSC or GOOG,
@@ -122,223 +318,154 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
* AWS is the default because most of non-amazon providers
* are still using aws:amz as a prefix.
*/
- tmp0 = data->set.str[STRING_AWS_SIGV4] ?
+ arg = data->set.str[STRING_AWS_SIGV4] ?
data->set.str[STRING_AWS_SIGV4] : "aws:amz";
- tmp1 = strchr(tmp0, ':');
- len = tmp1 ? (size_t)(tmp1 - tmp0) : strlen(tmp0);
- if(len < 1) {
- infof(data, "first provider can't be empty");
+
+ /* provider1[:provider2[:region[:service]]]
+
+ No string can be longer than N bytes of non-whitespace
+ */
+ (void)sscanf(arg, "%" MAX_SIGV4_LEN_TXT "[^:]"
+ ":%" MAX_SIGV4_LEN_TXT "[^:]"
+ ":%" MAX_SIGV4_LEN_TXT "[^:]"
+ ":%" MAX_SIGV4_LEN_TXT "s",
+ provider0, provider1, region, service);
+ if(!provider0[0]) {
+ failf(data, "first provider can't be empty");
ret = CURLE_BAD_FUNCTION_ARGUMENT;
goto fail;
}
- provider0_low = malloc(len + 1);
- provider0_up = malloc(len + 1);
- if(!provider0_low || !provider0_up) {
- goto fail;
- }
- Curl_strntolower(provider0_low, tmp0, len);
- provider0_low[len] = '\0';
- Curl_strntoupper(provider0_up, tmp0, len);
- provider0_up[len] = '\0';
+ else if(!provider1[0])
+ strcpy(provider1, provider0);
- if(tmp1) {
- tmp0 = tmp1 + 1;
- tmp1 = strchr(tmp0, ':');
- len = tmp1 ? (size_t)(tmp1 - tmp0) : strlen(tmp0);
- if(len < 1) {
- infof(data, "second provider can't be empty");
- ret = CURLE_BAD_FUNCTION_ARGUMENT;
- goto fail;
- }
- provider1_low = malloc(len + 1);
- provider1_mid = malloc(len + 1);
- if(!provider1_low || !provider1_mid) {
- goto fail;
- }
- Curl_strntolower(provider1_low, tmp0, len);
- provider1_low[len] = '\0';
- Curl_strntolower(provider1_mid, tmp0, len);
- provider1_mid[0] = Curl_raw_toupper(provider1_mid[0]);
- provider1_mid[len] = '\0';
-
- if(tmp1) {
- tmp0 = tmp1 + 1;
- tmp1 = strchr(tmp0, ':');
- len = tmp1 ? (size_t)(tmp1 - tmp0) : strlen(tmp0);
- if(len < 1) {
- infof(data, "region can't be empty");
- ret = CURLE_BAD_FUNCTION_ARGUMENT;
- goto fail;
- }
- region = Curl_memdup(tmp0, len + 1);
- if(!region) {
- goto fail;
- }
- region[len] = '\0';
-
- if(tmp1) {
- tmp0 = tmp1 + 1;
- service = strdup(tmp0);
- if(!service) {
- goto fail;
- }
- if(strlen(service) < 1) {
- infof(data, "service can't be empty");
- ret = CURLE_BAD_FUNCTION_ARGUMENT;
- goto fail;
- }
- }
- }
- }
- else {
- provider1_low = Curl_memdup(provider0_low, len + 1);
- provider1_mid = Curl_memdup(provider0_low, len + 1);
- if(!provider1_low || !provider1_mid) {
- goto fail;
- }
- provider1_mid[0] = Curl_raw_toupper(provider1_mid[0]);
- }
-
- if(!service) {
- tmp0 = hostname;
- tmp1 = strchr(tmp0, '.');
- if(!tmp1) {
- infof(data, "service missing in parameters or hostname");
+ if(!service[0]) {
+ char *hostdot = strchr(hostname, '.');
+ if(!hostdot) {
+ failf(data, "service missing in parameters and hostname");
ret = CURLE_URL_MALFORMAT;
goto fail;
}
- len = tmp1 - tmp0;
- service = Curl_memdup(tmp0, len + 1);
- if(!service) {
+ len = hostdot - hostname;
+ if(len > MAX_SIGV4_LEN) {
+ failf(data, "service too long in hostname");
+ ret = CURLE_URL_MALFORMAT;
goto fail;
}
+ strncpy(service, hostname, len);
service[len] = '\0';
- if(!region) {
- tmp0 = tmp1 + 1;
- tmp1 = strchr(tmp0, '.');
- if(!tmp1) {
- infof(data, "region missing in parameters or hostname");
+ if(!region[0]) {
+ const char *reg = hostdot + 1;
+ const char *hostreg = strchr(reg, '.');
+ if(!hostreg) {
+ failf(data, "region missing in parameters and hostname");
ret = CURLE_URL_MALFORMAT;
goto fail;
}
- len = tmp1 - tmp0;
- region = Curl_memdup(tmp0, len + 1);
- if(!region) {
+ len = hostreg - reg;
+ if(len > MAX_SIGV4_LEN) {
+ failf(data, "region too long in hostname");
+ ret = CURLE_URL_MALFORMAT;
goto fail;
}
+ strncpy(region, reg, len);
region[len] = '\0';
}
}
#ifdef DEBUGBUILD
- force_timestamp = getenv("CURL_FORCETIME");
- if(force_timestamp)
- clock = 0;
- else
- time(&clock);
+ {
+ char *force_timestamp = getenv("CURL_FORCETIME");
+ if(force_timestamp)
+ clock = 0;
+ else
+ time(&clock);
+ }
#else
time(&clock);
#endif
ret = Curl_gmtime(clock, &tm);
- if(ret != CURLE_OK) {
+ if(ret) {
goto fail;
}
if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) {
+ ret = CURLE_OUT_OF_MEMORY;
goto fail;
}
+
+ ret = make_headers(data, hostname, timestamp, provider1,
+ &date_header, &canonical_headers, &signed_headers);
+ if(ret)
+ goto fail;
+ ret = CURLE_OUT_OF_MEMORY;
+
memcpy(date, timestamp, sizeof(date));
date[sizeof(date) - 1] = 0;
- if(content_type) {
- content_type = strchr(content_type, ':');
- if(!content_type) {
- ret = CURLE_FAILED_INIT;
- goto fail;
- }
- content_type++;
- /* Skip whitespace now */
- while(*content_type == ' ' || *content_type == '\t')
- ++content_type;
-
- canonical_headers = curl_maprintf("content-type:%s\n"
- "host:%s\n"
- "x-%s-date:%s\n",
- content_type,
- hostname,
- provider1_low, timestamp);
- signed_headers = curl_maprintf("content-type;host;x-%s-date",
- provider1_low);
+ if(post_data) {
+ if(data->set.postfieldsize < 0)
+ post_data_len = strlen(post_data);
+ else
+ post_data_len = (size_t)data->set.postfieldsize;
}
- else {
- canonical_headers = curl_maprintf("host:%s\n"
- "x-%s-date:%s\n",
- hostname,
- provider1_low, timestamp);
- signed_headers = curl_maprintf("host;x-%s-date", provider1_low);
- }
-
- if(!canonical_headers || !signed_headers) {
- goto fail;
- }
-
- if(data->set.postfieldsize < 0)
- post_data_len = strlen(post_data);
- else
- post_data_len = (size_t)data->set.postfieldsize;
if(Curl_sha256it(sha_hash, (const unsigned char *) post_data,
- post_data_len)) {
+ post_data_len))
goto fail;
- }
sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex));
- Curl_http_method(data, conn, &method, &httpreq);
+ {
+ Curl_HttpReq httpreq;
+ const char *method;
- canonical_request =
- curl_maprintf("%s\n" /* HTTPRequestMethod */
- "%s\n" /* CanonicalURI */
- "%s\n" /* CanonicalQueryString */
- "%s\n" /* CanonicalHeaders */
- "%s\n" /* SignedHeaders */
- "%s", /* HashedRequestPayload in hex */
- method,
- data->state.up.path,
- data->state.up.query ? data->state.up.query : "",
- canonical_headers,
- signed_headers,
- sha_hex);
- if(!canonical_request) {
- goto fail;
+ Curl_http_method(data, conn, &method, &httpreq);
+
+ canonical_request =
+ curl_maprintf("%s\n" /* HTTPRequestMethod */
+ "%s\n" /* CanonicalURI */
+ "%s\n" /* CanonicalQueryString */
+ "%s\n" /* CanonicalHeaders */
+ "%s\n" /* SignedHeaders */
+ "%s", /* HashedRequestPayload in hex */
+ method,
+ data->state.up.path,
+ data->state.up.query ? data->state.up.query : "",
+ Curl_dyn_ptr(&canonical_headers),
+ Curl_dyn_ptr(&signed_headers),
+ sha_hex);
+ if(!canonical_request)
+ goto fail;
}
- request_type = curl_maprintf("%s4_request", provider0_low);
- if(!request_type) {
+ /* provider 0 lowercase */
+ Curl_strntolower(provider0, provider0, strlen(provider0));
+ request_type = curl_maprintf("%s4_request", provider0);
+ if(!request_type)
goto fail;
- }
credential_scope = curl_maprintf("%s/%s/%s/%s",
date, region, service, request_type);
- if(!credential_scope) {
+ if(!credential_scope)
goto fail;
- }
if(Curl_sha256it(sha_hash, (unsigned char *) canonical_request,
- strlen(canonical_request))) {
+ strlen(canonical_request)))
goto fail;
- }
sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex));
+ /* provider 0 uppercase */
+ Curl_strntoupper(provider0, provider0, strlen(provider0));
+
/*
- * Google allow to use rsa key instead of HMAC, so this code might change
- * In the future, but for now we support only HMAC version
+ * Google allows using RSA key instead of HMAC, so this code might change
+ * in the future. For now we ony support HMAC.
*/
str_to_sign = curl_maprintf("%s4-HMAC-SHA256\n" /* Algorithm */
"%s\n" /* RequestDateTime */
"%s\n" /* CredentialScope */
"%s", /* HashedCanonicalRequest in hex */
- provider0_up,
+ provider0,
timestamp,
credential_scope,
sha_hex);
@@ -346,36 +473,33 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
goto fail;
}
- secret = curl_maprintf("%s4%s", provider0_up, passwd);
- if(!secret) {
+ /* provider 0 uppercase */
+ secret = curl_maprintf("%s4%s", provider0,
+ data->state.aptr.passwd ?
+ data->state.aptr.passwd : "");
+ if(!secret)
goto fail;
- }
- HMAC_SHA256(secret, strlen(secret),
- date, strlen(date), tmp_sign0);
- HMAC_SHA256(tmp_sign0, sizeof(tmp_sign0),
- region, strlen(region), tmp_sign1);
- HMAC_SHA256(tmp_sign1, sizeof(tmp_sign1),
- service, strlen(service), tmp_sign0);
- HMAC_SHA256(tmp_sign0, sizeof(tmp_sign0),
- request_type, strlen(request_type), tmp_sign1);
- HMAC_SHA256(tmp_sign1, sizeof(tmp_sign1),
- str_to_sign, strlen(str_to_sign), tmp_sign0);
+ HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0);
+ HMAC_SHA256(sign0, sizeof(sign0), region, strlen(region), sign1);
+ HMAC_SHA256(sign1, sizeof(sign1), service, strlen(service), sign0);
+ HMAC_SHA256(sign0, sizeof(sign0), request_type, strlen(request_type), sign1);
+ HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0);
- sha256_to_hex(sha_hex, tmp_sign0, sizeof(sha_hex));
+ sha256_to_hex(sha_hex, sign0, sizeof(sha_hex));
+ /* provider 0 uppercase */
auth_headers = curl_maprintf("Authorization: %s4-HMAC-SHA256 "
"Credential=%s/%s, "
"SignedHeaders=%s, "
"Signature=%s\r\n"
- "X-%s-Date: %s\r\n",
- provider0_up,
+ "%s\r\n",
+ provider0,
user,
credential_scope,
- signed_headers,
+ Curl_dyn_ptr(&signed_headers),
sha_hex,
- provider1_mid,
- timestamp);
+ date_header);
if(!auth_headers) {
goto fail;
}
@@ -386,19 +510,14 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
ret = CURLE_OK;
fail:
- free(provider0_low);
- free(provider0_up);
- free(provider1_low);
- free(provider1_mid);
- free(region);
- free(service);
- free(canonical_headers);
- free(signed_headers);
+ Curl_dyn_free(&canonical_headers);
+ Curl_dyn_free(&signed_headers);
free(canonical_request);
free(request_type);
free(credential_scope);
free(str_to_sign);
free(secret);
+ free(date_header);
return ret;
}
diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc
index 20cdb9c8e4..7fe87f4359 100644
--- a/tests/data/Makefile.inc
+++ b/tests/data/Makefile.inc
@@ -225,6 +225,7 @@ test1916 test1917 test1918 test1919 \
\
test1933 test1934 test1935 test1936 test1937 test1938 test1939 test1940 \
test1941 test1942 test1943 test1944 test1945 test1946 test1947 test1948 \
+test1955 \
\
test2000 test2001 test2002 test2003 test2004 \
\
diff --git a/tests/data/test1933 b/tests/data/test1933
index d2aabb1fed..3f3e8ac2cd 100644
--- a/tests/data/test1933
+++ b/tests/data/test1933
@@ -47,7 +47,7 @@ lib%TESTNUMBER
-http://xxx:yyy@%HOSTIP:%HTTPPORT/%TESTNUMBER/testapi/test
+http://xxx:yyy@127.0.0.1:9000/%TESTNUMBER/testapi/test 127.0.0.1:9000:%HOSTIP:%HTTPPORT
@@ -60,8 +60,8 @@ http://xxx:yyy@%HOSTIP:%HTTPPORT/%TESTNUMBER/testapi/test
GET /%TESTNUMBER/testapi/test HTTP/1.1
-Host: %HOSTIP:%HTTPPORT
-Authorization: XXX4-HMAC-SHA256 Credential=xxx/19700101/0/127/xxx4_request, SignedHeaders=content-type;host;x-xxx-date, Signature=d2c2dff48c59ec49dc31ef94f18c5dc1ac3eae2a70d51633a4342dadc0683664
+Host: 127.0.0.1:9000
+Authorization: XXX4-HMAC-SHA256 Credential=xxx/19700101/0/127/xxx4_request, SignedHeaders=content-type;host;x-xxx-date, Signature=3d8e00a02e437211a596143dcd590fcc805b731365c68f7f48951ea6eda39c4f
X-Xxx-Date: 19700101T000000Z
diff --git a/tests/data/test1934 b/tests/data/test1934
index 35fa318c11..a131df49e7 100644
--- a/tests/data/test1934
+++ b/tests/data/test1934
@@ -47,7 +47,7 @@ lib%TESTNUMBER
-http://%HOSTIP:%HTTPPORT/%TESTNUMBER/testapi/test
+http://127.0.0.1:9000/%TESTNUMBER/testapi/test 127.0.0.1:9000:%HOSTIP:%HTTPPORT
@@ -60,8 +60,8 @@ http://%HOSTIP:%HTTPPORT/%TESTNUMBER/testapi/test
GET /%TESTNUMBER/testapi/test HTTP/1.1
-Host: %HOSTIP:%HTTPPORT
-Authorization: XXX4-HMAC-SHA256 Credential=xxx/19700101/0/127/xxx4_request, SignedHeaders=content-type;host;x-yyy-date, Signature=938937ca7da6bb3dbf15e30928265ec6f061532d035d2afda92fa7cb10feb196
+Host: 127.0.0.1:9000
+Authorization: XXX4-HMAC-SHA256 Credential=xxx/19700101/0/127/xxx4_request, SignedHeaders=content-type;host;x-yyy-date, Signature=cf8dc9a4af903a1a9bb1385d8e2366d780afb501e266436598438395e502d58c
X-Yyy-Date: 19700101T000000Z
diff --git a/tests/data/test1935 b/tests/data/test1935
index 6d48f325cc..d39a864715 100644
--- a/tests/data/test1935
+++ b/tests/data/test1935
@@ -47,7 +47,7 @@ lib%TESTNUMBER
-http://%HOSTIP:%HTTPPORT/%TESTNUMBER/testapi/test
+http://127.0.0.1:9000/%TESTNUMBER/testapi/test 127.0.0.1:9000:%HOSTIP:%HTTPPORT
@@ -60,8 +60,8 @@ http://%HOSTIP:%HTTPPORT/%TESTNUMBER/testapi/test
GET /%TESTNUMBER/testapi/test HTTP/1.1
-Host: %HOSTIP:%HTTPPORT
-Authorization: XXX4-HMAC-SHA256 Credential=xxx/19700101/rrr/127/xxx4_request, SignedHeaders=content-type;host;x-yyy-date, Signature=240750deb9263d4c8ece71c016f3919b56e518249390ef075740f94ef8df846f
+Host: 127.0.0.1:9000
+Authorization: XXX4-HMAC-SHA256 Credential=xxx/19700101/rrr/127/xxx4_request, SignedHeaders=content-type;host;x-yyy-date, Signature=a0b11b97b54689428d4188b788ed32865d607822d85d3e91cf06141f479dac0b
X-Yyy-Date: 19700101T000000Z
diff --git a/tests/data/test1936 b/tests/data/test1936
index 366abd64b2..a0d38403b7 100644
--- a/tests/data/test1936
+++ b/tests/data/test1936
@@ -47,7 +47,7 @@ lib%TESTNUMBER
-http://%HOSTIP:%HTTPPORT/%TESTNUMBER/testapi/test
+http://127.0.0.1:9000/%TESTNUMBER/testapi/test 127.0.0.1:9000:%HOSTIP:%HTTPPORT
@@ -60,8 +60,8 @@ http://%HOSTIP:%HTTPPORT/%TESTNUMBER/testapi/test
GET /%TESTNUMBER/testapi/test HTTP/1.1
-Host: %HOSTIP:%HTTPPORT
-Authorization: XXX4-HMAC-SHA256 Credential=xxx/19700101/rrr/sss/xxx4_request, SignedHeaders=content-type;host;x-yyy-date, Signature=f32cf87977cea5d3274b524b53e5d28f4aac54c372f710ae0cc3a9ececaf169f
+Host: 127.0.0.1:9000
+Authorization: XXX4-HMAC-SHA256 Credential=xxx/19700101/rrr/sss/xxx4_request, SignedHeaders=content-type;host;x-yyy-date, Signature=026b713d76b0789bd224c5e41322f74eed088f8a22fd15183ca68376c575c5b0
X-Yyy-Date: 19700101T000000Z
diff --git a/tests/data/test1937 b/tests/data/test1937
index e24445ac6a..ca1214b835 100644
--- a/tests/data/test1937
+++ b/tests/data/test1937
@@ -48,7 +48,7 @@ lib%TESTNUMBER
-http://%HOSTIP:%HTTPPORT/%TESTNUMBER/testapi/test
+http://127.0.0.1:9000/%TESTNUMBER/testapi/test 127.0.0.1:9000:%HOSTIP:%HTTPPORT
@@ -61,8 +61,8 @@ http://%HOSTIP:%HTTPPORT/%TESTNUMBER/testapi/test
POST /%TESTNUMBER/testapi/test HTTP/1.1
-Host: %HOSTIP:%HTTPPORT
-Authorization: PROVIDER14-HMAC-SHA256 Credential=keyId/19700101/region/service/provider14_request, SignedHeaders=content-type;host;x-provider2-date, Signature=391e410177d0e9ee80728082446ef69d6b29157fe71f8b4805fce7c186fd956d
+Host: 127.0.0.1:9000
+Authorization: PROVIDER14-HMAC-SHA256 Credential=keyId/19700101/region/service/provider14_request, SignedHeaders=content-type;host;x-provider2-date, Signature=4928ccf97a9e71fe27f91db5a3b3c943b6080d25e6f4df8593d4c38e7d1e849b
X-Provider2-Date: 19700101T000000Z
Content-Length: 8
diff --git a/tests/data/test1938 b/tests/data/test1938
index 5341de00f3..565a54ffe6 100644
Binary files a/tests/data/test1938 and b/tests/data/test1938 differ
diff --git a/tests/data/test1955 b/tests/data/test1955
new file mode 100644
index 0000000000..5ebbc54bc8
--- /dev/null
+++ b/tests/data/test1955
@@ -0,0 +1,75 @@
+
+
+
+HTTP
+CURLOPT_AWS_SIGV4
+
+
+
+# Server-side
+
+
+HTTP/1.1 302 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Type: text/html
+Content-Length: 0
+Location: /%TESTNUMBER0002
+
+
+
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Type: text/html
+Content-Length: 0
+
+
+
+
+# Client-side
+
+
+http
+
+# this relies on the debug feature which allow to set the time
+
+SSL
+debug
+crypto
+
+
+CURL_FORCEHOST=1
+
+
+
+HTTP AWS_SIGV4 with X-Xxx-Content-Sha256
+
+
+lib%TESTNUMBER
+
+
+
+http://exam.ple.com:9000/%TESTNUMBER/testapi/test exam.ple.com:9000:%HOSTIP:%HTTPPORT
+
+
+
+# Verify data after the test has been "shot"
+
+
+^User-Agent:.*
+^Content-Type:.*
+^Accept:.*
+
+
+GET /%TESTNUMBER/testapi/test HTTP/1.1
+Host: exam.ple.com:9000
+Authorization: XXX4-HMAC-SHA256 Credential=xxx/19700101/ple/exam/xxx4_request, SignedHeaders=content-type;host;tesmixcase;test0;test1;test2;test_space;x-xxx-date, Signature=819251feec8de52dfaa992320241f23d27cefa979c93e039ae7df03ac486ed16
+X-Xxx-Date: 19700101T000000Z
+test2: 1234
+test_space: t s m end
+tesMixCase: MixCase
+
+
+
+
diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc
index 3b9cdd0069..484ac178c7 100644
--- a/tests/libtest/Makefile.inc
+++ b/tests/libtest/Makefile.inc
@@ -64,7 +64,7 @@ noinst_PROGRAMS = chkhostname libauthretry libntlmconnect \
lib1905 lib1906 lib1907 lib1908 lib1910 lib1911 lib1912 lib1913 \
lib1915 lib1916 lib1917 lib1918 lib1919 \
lib1933 lib1934 lib1935 lib1936 lib1937 lib1938 lib1939 lib1940 \
- lib1945 lib1946 lib1947 lib1948 \
+ lib1945 lib1946 lib1947 lib1948 lib1955 \
lib2301 lib2302 \
lib3010 lib3025 lib3026 lib3027
@@ -757,6 +757,10 @@ lib1948_SOURCES = lib1948.c $(SUPPORTFILES)
lib1948_LDADD = $(TESTUTIL_LIBS)
lib1948_CPPFLAGS = $(AM_CPPFLAGS)
+lib1955_SOURCES = lib1955.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
+lib1955_LDADD = $(TESTUTIL_LIBS)
+lib1955_CPPFLAGS = $(AM_CPPFLAGS)
+
lib2301_SOURCES = lib2301.c $(SUPPORTFILES)
lib2301_LDADD = $(TESTUTIL_LIBS)
diff --git a/tests/libtest/lib1933.c b/tests/libtest/lib1933.c
index 3ff2dc17f1..39459d001c 100644
--- a/tests/libtest/lib1933.c
+++ b/tests/libtest/lib1933.c
@@ -29,6 +29,7 @@ int test(char *URL)
{
CURL *curl;
CURLcode res = TEST_ERR_MAJOR_BAD;
+ struct curl_slist *connect_to = NULL;
struct curl_slist *list = NULL;
if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
@@ -47,6 +48,10 @@ int test(char *URL)
test_setopt(curl, CURLOPT_AWS_SIGV4, "xxx");
test_setopt(curl, CURLOPT_HEADER, 0L);
test_setopt(curl, CURLOPT_URL, URL);
+ if(libtest_arg2) {
+ connect_to = curl_slist_append(connect_to, libtest_arg2);
+ }
+ test_setopt(curl, CURLOPT_CONNECT_TO, connect_to);
list = curl_slist_append(list, "Content-Type: application/json");
test_setopt(curl, CURLOPT_HTTPHEADER, list);
@@ -54,6 +59,7 @@ int test(char *URL)
test_cleanup:
+ curl_slist_free_all(connect_to);
curl_slist_free_all(list);
curl_easy_cleanup(curl);
curl_global_cleanup();
diff --git a/tests/libtest/lib1934.c b/tests/libtest/lib1934.c
index d5f3884448..917f6e681d 100644
--- a/tests/libtest/lib1934.c
+++ b/tests/libtest/lib1934.c
@@ -29,6 +29,7 @@ int test(char *URL)
{
CURL *curl;
CURLcode res = TEST_ERR_MAJOR_BAD;
+ struct curl_slist *connect_to = NULL;
struct curl_slist *list = NULL;
if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
@@ -48,6 +49,10 @@ int test(char *URL)
test_setopt(curl, CURLOPT_USERPWD, "xxx:yyy");
test_setopt(curl, CURLOPT_HEADER, 0L);
test_setopt(curl, CURLOPT_URL, URL);
+ if(libtest_arg2) {
+ connect_to = curl_slist_append(connect_to, libtest_arg2);
+ }
+ test_setopt(curl, CURLOPT_CONNECT_TO, connect_to);
list = curl_slist_append(list, "Content-Type: application/json");
test_setopt(curl, CURLOPT_HTTPHEADER, list);
@@ -55,6 +60,7 @@ int test(char *URL)
test_cleanup:
+ curl_slist_free_all(connect_to);
curl_slist_free_all(list);
curl_easy_cleanup(curl);
curl_global_cleanup();
diff --git a/tests/libtest/lib1935.c b/tests/libtest/lib1935.c
index e27be122eb..459ead3935 100644
--- a/tests/libtest/lib1935.c
+++ b/tests/libtest/lib1935.c
@@ -29,6 +29,7 @@ int test(char *URL)
{
CURL *curl;
CURLcode res = TEST_ERR_MAJOR_BAD;
+ struct curl_slist *connect_to = NULL;
struct curl_slist *list = NULL;
if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
@@ -48,6 +49,10 @@ int test(char *URL)
test_setopt(curl, CURLOPT_USERPWD, "xxx:yyy");
test_setopt(curl, CURLOPT_HEADER, 0L);
test_setopt(curl, CURLOPT_URL, URL);
+ if(libtest_arg2) {
+ connect_to = curl_slist_append(connect_to, libtest_arg2);
+ }
+ test_setopt(curl, CURLOPT_CONNECT_TO, connect_to);
list = curl_slist_append(list, "Content-Type: application/json");
test_setopt(curl, CURLOPT_HTTPHEADER, list);
@@ -55,6 +60,7 @@ int test(char *URL)
test_cleanup:
+ curl_slist_free_all(connect_to);
curl_slist_free_all(list);
curl_easy_cleanup(curl);
curl_global_cleanup();
diff --git a/tests/libtest/lib1936.c b/tests/libtest/lib1936.c
index 703e7403ae..e522e3817b 100644
--- a/tests/libtest/lib1936.c
+++ b/tests/libtest/lib1936.c
@@ -29,6 +29,7 @@ int test(char *URL)
{
CURL *curl;
CURLcode res = TEST_ERR_MAJOR_BAD;
+ struct curl_slist *connect_to = NULL;
struct curl_slist *list = NULL;
if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
@@ -48,6 +49,10 @@ int test(char *URL)
test_setopt(curl, CURLOPT_USERPWD, "xxx:yyy");
test_setopt(curl, CURLOPT_HEADER, 0L);
test_setopt(curl, CURLOPT_URL, URL);
+ if(libtest_arg2) {
+ connect_to = curl_slist_append(connect_to, libtest_arg2);
+ }
+ test_setopt(curl, CURLOPT_CONNECT_TO, connect_to);
list = curl_slist_append(list, "Content-Type: application/json");
test_setopt(curl, CURLOPT_HTTPHEADER, list);
@@ -55,6 +60,7 @@ int test(char *URL)
test_cleanup:
+ curl_slist_free_all(connect_to);
curl_slist_free_all(list);
curl_easy_cleanup(curl);
curl_global_cleanup();
diff --git a/tests/libtest/lib1937.c b/tests/libtest/lib1937.c
index b2d4518a9c..14b9687436 100644
--- a/tests/libtest/lib1937.c
+++ b/tests/libtest/lib1937.c
@@ -30,6 +30,7 @@ int test(char *URL)
{
CURL *curl;
CURLcode res = TEST_ERR_MAJOR_BAD;
+ struct curl_slist *connect_to = NULL;
struct curl_slist *list = NULL;
if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
@@ -50,6 +51,10 @@ int test(char *URL)
test_setopt(curl, CURLOPT_USERPWD, "keyId:SecretKey");
test_setopt(curl, CURLOPT_HEADER, 0L);
test_setopt(curl, CURLOPT_URL, URL);
+ if(libtest_arg2) {
+ connect_to = curl_slist_append(connect_to, libtest_arg2);
+ }
+ test_setopt(curl, CURLOPT_CONNECT_TO, connect_to);
list = curl_slist_append(list, "Content-Type: application/json");
test_setopt(curl, CURLOPT_HTTPHEADER, list);
test_setopt(curl, CURLOPT_POSTFIELDS, "postData");
@@ -58,6 +63,7 @@ int test(char *URL)
test_cleanup:
+ curl_slist_free_all(connect_to);
curl_slist_free_all(list);
curl_easy_cleanup(curl);
curl_global_cleanup();
diff --git a/tests/libtest/lib1938.c b/tests/libtest/lib1938.c
index 3bbde8c10c..79dc6cc0ec 100644
--- a/tests/libtest/lib1938.c
+++ b/tests/libtest/lib1938.c
@@ -30,6 +30,7 @@ int test(char *URL)
{
CURL *curl;
CURLcode res = TEST_ERR_MAJOR_BAD;
+ struct curl_slist *connect_to = NULL;
struct curl_slist *list = NULL;
unsigned char data[] = {0x70, 0x6f, 0x73, 0x74, 0, 0x44, 0x61, 0x74, 0x61};
@@ -51,6 +52,10 @@ int test(char *URL)
test_setopt(curl, CURLOPT_USERPWD, "keyId:SecretKey");
test_setopt(curl, CURLOPT_HEADER, 0L);
test_setopt(curl, CURLOPT_URL, URL);
+ if(libtest_arg2) {
+ connect_to = curl_slist_append(connect_to, libtest_arg2);
+ }
+ test_setopt(curl, CURLOPT_CONNECT_TO, connect_to);
list = curl_slist_append(list, "Content-Type: application/json");
test_setopt(curl, CURLOPT_HTTPHEADER, list);
test_setopt(curl, CURLOPT_POSTFIELDS, data);
@@ -60,6 +65,7 @@ int test(char *URL)
test_cleanup:
+ curl_slist_free_all(connect_to);
curl_slist_free_all(list);
curl_easy_cleanup(curl);
curl_global_cleanup();
diff --git a/tests/libtest/lib1955.c b/tests/libtest/lib1955.c
new file mode 100644
index 0000000000..053c3e1ae2
--- /dev/null
+++ b/tests/libtest/lib1955.c
@@ -0,0 +1,76 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "test.h"
+
+#include "memdebug.h"
+
+int test(char *URL)
+{
+ CURL *curl;
+ CURLcode res = TEST_ERR_MAJOR_BAD;
+ struct curl_slist *list = NULL;
+ struct curl_slist *connect_to = NULL;
+
+ if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
+ fprintf(stderr, "curl_global_init() failed\n");
+ return TEST_ERR_MAJOR_BAD;
+ }
+
+ curl = curl_easy_init();
+ if(!curl) {
+ fprintf(stderr, "curl_easy_init() failed\n");
+ curl_global_cleanup();
+ return TEST_ERR_MAJOR_BAD;
+ }
+
+ test_setopt(curl, CURLOPT_VERBOSE, 1L);
+ test_setopt(curl, CURLOPT_AWS_SIGV4, "xxx");
+ test_setopt(curl, CURLOPT_USERPWD, "xxx");
+ test_setopt(curl, CURLOPT_HEADER, 0L);
+ test_setopt(curl, CURLOPT_URL, URL);
+ list = curl_slist_append(list, "test2: 1234");
+ if(!list)
+ goto test_cleanup;
+ if(libtest_arg2) {
+ connect_to = curl_slist_append(connect_to, libtest_arg2);
+ }
+ test_setopt(curl, CURLOPT_CONNECT_TO, connect_to);
+ curl_slist_append(list, "Content-Type: application/json");
+ curl_slist_append(list, "test1:");
+ curl_slist_append(list, "test0");
+ curl_slist_append(list, "test_space: t\ts m\t end ");
+ curl_slist_append(list, "tesMixCase: MixCase");
+ test_setopt(curl, CURLOPT_HTTPHEADER, list);
+
+ res = curl_easy_perform(curl);
+
+test_cleanup:
+
+ curl_slist_free_all(connect_to);
+ curl_slist_free_all(list);
+ curl_easy_cleanup(curl);
+ curl_global_cleanup();
+
+ return res;
+}