Skip to content

Commit 7290ec8

Browse files
SNOW-1526511: unhardcode top level domain (#732)
1 parent b19db15 commit 7290ec8

File tree

9 files changed

+187
-42
lines changed

9 files changed

+187
-42
lines changed

deps/curl/lib/vtls/sf_ocsp.c

+61-11
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@
4141
#include <direct.h>
4242
#include <time.h>
4343

44+
#include <Shellapi.h>
45+
#define strncasecmp _strnicmp
46+
#define strcasecmp _stricmp
47+
4448
typedef HANDLE SF_THREAD_HANDLE;
4549
typedef CONDITION_VARIABLE SF_CONDITION_HANDLE;
4650
typedef CRITICAL_SECTION SF_CRITICAL_SECTION_HANDLE;
@@ -76,10 +80,11 @@ typedef pthread_mutex_t SF_MUTEX_HANDLE;
7680
#define strcasecmp _stricmp
7781
#endif
7882

79-
#define DEFAULT_OCSP_RESPONSE_CACHE_HOST "http://ocsp.snowflakecomputing.com"
83+
#define DEFAULT_OCSP_RESPONSE_CACHE_HOST "http://ocsp.snowflakecomputing.%s"
8084
#define OCSP_RESPONSE_CACHE_JSON "ocsp_response_cache.json"
8185
#define OCSP_RESPONSE_CACHE_URL "%s/%s"
82-
#define OCSP_RESPONDER_RETRY_URL "http://ocsp.snowflakecomputing.com/retry"
86+
#define OCSP_RESPONDER_RETRY_URL "http://ocsp.snowflakecomputing.%s/retry"
87+
#define MAX_DOMAIN_LEN 64 //max 63 characters + terminator
8388

8489
#define GET_STR_OCSP_LOG(X,Y) X->Y ? sf_curl_cJSON_CreateString(X->Y) : NULL
8590
#define GET_BOOL_OCSP_LOG(X,Y) X->Y ? sf_curl_cJSON_CreateString("True") : sf_curl_cJSON_CreateString("False")
@@ -227,6 +232,28 @@ static char ocsp_cache_server_url[MAX_BUFFER_LENGTH] = "";
227232

228233
static char ocsp_cache_server_retry_url_pattern[MAX_BUFFER_LENGTH];
229234

235+
static char default_ocsp_cache_host[sizeof(DEFAULT_OCSP_RESPONSE_CACHE_HOST) + MAX_DOMAIN_LEN] = "";
236+
237+
static char default_ocsp_cache_retry_url[sizeof(OCSP_RESPONDER_RETRY_URL) + MAX_DOMAIN_LEN] = "";
238+
239+
// functions for test purpose only
240+
SF_PUBLIC(CURLcode) checkTelemetryHosts(char *hostname)
241+
{
242+
struct connectdata conn;
243+
conn.host.name = hostname;
244+
return checkCertOCSP(&conn, NULL, NULL, NULL, 0, 0);
245+
}
246+
247+
void get_cache_server_url(char* buf, size_t bufsize)
248+
{
249+
strncpy(buf, ocsp_cache_server_url, bufsize);
250+
}
251+
252+
void get_cache_retry_url_pattern(char* buf, size_t bufsize)
253+
{
254+
strncpy(buf, ocsp_cache_server_retry_url_pattern, bufsize);
255+
}
256+
230257
/* Mutex */
231258
int _mutex_init(SF_MUTEX_HANDLE *lock) {
232259
#ifdef _WIN32
@@ -2244,10 +2271,24 @@ void initOCSPCacheServer(struct Curl_easy *data)
22442271

22452272
if (ocsp_cache_server_url_env == NULL)
22462273
{
2274+
char* top_domain = strrchr(data->conn->host.name, '.');
2275+
if (top_domain)
2276+
{
2277+
top_domain++;
2278+
}
2279+
else
2280+
{
2281+
// It's basically impossible not finding top domain in host.
2282+
// Use "com" as default just in case.
2283+
top_domain = "com";
2284+
}
2285+
22472286
/* default URL */
2287+
snprintf(default_ocsp_cache_host, sizeof(default_ocsp_cache_host),
2288+
DEFAULT_OCSP_RESPONSE_CACHE_HOST, top_domain);
22482289
snprintf(ocsp_cache_server_url, sizeof(ocsp_cache_server_url),
22492290
OCSP_RESPONSE_CACHE_URL,
2250-
DEFAULT_OCSP_RESPONSE_CACHE_HOST,
2291+
default_ocsp_cache_host,
22512292
OCSP_RESPONSE_CACHE_JSON);
22522293

22532294
if (!ACTIVATE_SSD)
@@ -2261,9 +2302,11 @@ void initOCSPCacheServer(struct Curl_easy *data)
22612302
* Non private link customers always go to default
22622303
* retry URL for OCSP retries
22632304
*/
2305+
snprintf(default_ocsp_cache_retry_url, sizeof(default_ocsp_cache_retry_url),
2306+
OCSP_RESPONDER_RETRY_URL, top_domain);
22642307
strncpy(ocsp_cache_server_retry_url_pattern,
2265-
OCSP_RESPONDER_RETRY_URL,
2266-
sizeof(OCSP_RESPONDER_RETRY_URL));
2308+
default_ocsp_cache_retry_url,
2309+
strlen(default_ocsp_cache_retry_url) + 1);
22672310
}
22682311
}
22692312
else
@@ -2360,15 +2403,22 @@ SF_PUBLIC(CURLcode) checkCertOCSP(struct connectdata *conn,
23602403
SF_FAILOPEN_STATUS ocsp_fail_open = ENABLED;
23612404
char last_timeout_host[MAX_BUFFER_LENGTH];
23622405
last_timeout_host[0] = '\0';
2363-
2406+
// SNOW-1526511 ignore top level domain name to be more flexible
2407+
const char* telemetry_endpoints[] = {
2408+
"sfctest.client-telemetry.snowflakecomputing.",
2409+
"sfcdev.client-telemetry.snowflakecomputing.",
2410+
"client-telemetry.snowflakecomputing."
2411+
};
2412+
const int telemetry_endpoints_num = sizeof(telemetry_endpoints) / sizeof(char*);
23642413

23652414
// These end points are Out of band telemetry end points.
23662415
// Do not use OCSP/failsafe on Out of band telemetry endpoints
2367-
if ( (strcmp(conn->host.name, "sfctest.client-telemetry.snowflakecomputing.com") == 0 )
2368-
|| (strcmp(conn->host.name, "sfcdev.client-telemetry.snowflakecomputing.com") == 0 )
2369-
|| (strcmp(conn->host.name, "client-telemetry.snowflakecomputing.com") == 0 )
2370-
) {
2371-
return rs;
2416+
for (int i = 0; i < telemetry_endpoints_num; i++)
2417+
{
2418+
if (strncasecmp(conn->host.name, telemetry_endpoints[i], strlen(telemetry_endpoints[i])) == 0)
2419+
{
2420+
return rs;
2421+
}
23722422
}
23732423

23742424
SF_OTD ocsp_log_data;

lib/client.c

+32-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
2020
#include "authenticator.h"
2121
#include "query_context_cache.h"
2222

23+
#ifdef _WIN32
24+
#include <Shellapi.h>
25+
#define strncasecmp _strnicmp
26+
#define strcasecmp _stricmp
27+
#endif
28+
2329
#define curl_easier_escape(curl, string) curl_easy_escape(curl, string, 0)
2430

2531
// Define internal constants
@@ -432,14 +438,38 @@ _snowflake_check_connection_parameters(SF_CONNECT *sf) {
432438
// construct a host parameter if not specified,
433439
char buf[1024];
434440
if (sf->region) {
435-
sf_sprintf(buf, sizeof(buf), "%s.%s.snowflakecomputing.com",
436-
sf->account, sf->region);
441+
if (strncasecmp(sf->region, "cn-", 3) == 0)
442+
{
443+
//region started with "cn-", use "cn" for top domain
444+
sf_sprintf(buf, sizeof(buf), "%s.%s.snowflakecomputing.cn",
445+
sf->account, sf->region);
446+
}
447+
else
448+
{
449+
sf_sprintf(buf, sizeof(buf), "%s.%s.snowflakecomputing.com",
450+
sf->account, sf->region);
451+
}
437452
} else {
438453
sf_sprintf(buf, sizeof(buf), "%s.snowflakecomputing.com",
439454
sf->account);
440455
}
441456
alloc_buffer_and_copy(&sf->host, buf);
442457
}
458+
459+
char* top_domain = strrchr(sf->host, '.');
460+
if (top_domain)
461+
{
462+
top_domain++;
463+
}
464+
else
465+
{
466+
// It's basically impossible not finding top domain in host.
467+
// Log the entire host just in case.
468+
top_domain = sf->host;
469+
}
470+
471+
log_info("Connecting to %s Snowflake domain", (strcasecmp(top_domain, "cn") == 0) ? "CHINA" : "GLOBAL");
472+
443473
// split account and region if connected by a dot.
444474
char *dot_ptr = strchr(sf->account, (int) '.');
445475
if (dot_ptr) {

lib/client_int.h

-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
#define HEADER_CLIENT_APP_ID_FORMAT "CLIENT_APP_ID: %s"
2222
#define HEADER_CLIENT_APP_VERSION_FORMAT "CLIENT_APP_VERSION: %s"
2323

24-
#define DEFAULT_SNOWFLAKE_BASE_URL "snowflakecomputing.com"
2524
#define DEFAULT_SNOWFLAKE_REQUEST_TIMEOUT 60
2625

2726
#define SESSION_URL "/session/v1/login-request"

lib/connection.c

+17-23
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,6 @@ get_next_sleep_with_jitter(DECORRELATE_JITTER_BACKOFF *djb, uint32 sleep, uint64
565565

566566
char * STDCALL encode_url(CURL *curl,
567567
const char *protocol,
568-
const char *account,
569568
const char *host,
570569
const char *port,
571570
const char *url,
@@ -594,34 +593,29 @@ char * STDCALL encode_url(CURL *curl,
594593
const char *amp = "&";
595594
size_t amp_size = strlen(amp);
596595

596+
if (host_empty)
597+
{
598+
SET_SNOWFLAKE_ERROR(error, SF_STATUS_ERROR_BAD_CONNECTION_PARAMS,
599+
"Invalid host trying to create encoded url",
600+
SF_SQLSTATE_UNABLE_TO_CONNECT);
601+
goto cleanup;
602+
}
603+
597604
// Set proper format based on variables passed into encode URL.
598605
// The format includes format specifiers that will be consumed by empty fields
599606
// (i.e if port is empty, add an extra specifier so that we have 1 call to snprintf, vs. 4 different calls)
600-
// Format specifier order is protocol, then account, then host, then port, then url.
607+
// Format specifier order is protocol, then then host, then port, then url.
601608
// Base size increases reflect the number of static characters in the format string (i.e. ':', '/', '.')
602-
if (!port_empty && !host_empty) {
603-
format = "%s://%s%s:%s%s";
609+
if (!port_empty) {
610+
format = "%s://%s:%s%s";
604611
base_url_size += 4;
605-
// Set account to an empty string since host overwrites account
606-
account = "";
607-
} else if (port_empty && !host_empty) {
608-
format = "%s://%s%s%s%s";
609-
base_url_size += 3;
610-
port = "";
611-
// Set account to an empty string since host overwrites account
612-
account = "";
613-
} else if (!port_empty && host_empty) {
614-
format = "%s://%s.%s:%s%s";
615-
base_url_size += 5;
616-
host = DEFAULT_SNOWFLAKE_BASE_URL;
617612
} else {
618-
format = "%s://%s.%s%s%s";
619-
base_url_size += 4;
620-
host = DEFAULT_SNOWFLAKE_BASE_URL;
613+
format = "%s://%s%s%s";
614+
base_url_size += 3;
621615
port = "";
622616
}
623617
base_url_size +=
624-
strlen(protocol) + strlen(account) + strlen(host) + strlen(port) +
618+
strlen(protocol) + strlen(host) + strlen(port) +
625619
strlen(url) + strlen(URL_QUERY_DELIMITER);
626620

627621
encoded_url_size = base_url_size;
@@ -660,7 +654,7 @@ char * STDCALL encode_url(CURL *curl,
660654
SF_SQLSTATE_UNABLE_TO_CONNECT);
661655
goto cleanup;
662656
}
663-
sf_sprintf(encoded_url, base_url_size, format, protocol, account, host, port,
657+
sf_sprintf(encoded_url, base_url_size, format, protocol, host, port,
664658
url);
665659

666660
// Initially add the query delimiter "?"
@@ -931,7 +925,7 @@ sf_bool STDCALL request(SF_CONNECT *sf,
931925
}
932926
}
933927

934-
encoded_url = encode_url(curl, sf->protocol, sf->account, sf->host,
928+
encoded_url = encode_url(curl, sf->protocol, sf->host,
935929
sf->port, url, url_params, num_url_params,
936930
error, sf->directURL_param);
937931
if (encoded_url == NULL) {
@@ -1015,7 +1009,7 @@ sf_bool STDCALL renew_session(CURL *curl, SF_CONNECT *sf, SF_ERROR_STRUCT *error
10151009
// Create request id, set in url parameter and encode url
10161010
uuid4_generate(request_id);
10171011
url_params[0].value = request_id;
1018-
encoded_url = encode_url(curl, sf->protocol, sf->account, sf->host,
1012+
encoded_url = encode_url(curl, sf->protocol, sf->host,
10191013
sf->port, RENEW_SESSION_URL, url_params, 1, error,
10201014
sf->directURL_param);
10211015
if (!encoded_url) {

lib/connection.h

+2-3
Original file line numberDiff line numberDiff line change
@@ -281,8 +281,7 @@ uint32 get_next_sleep_with_jitter(DECORRELATE_JITTER_BACKOFF *djb, uint32 sleep,
281281
*
282282
* @param curl cURL object for the request.
283283
* @param protocol Protocol to use in the request. Either HTTP or HTTPS.
284-
* @param account Snowflake account name. This should be the account of the user.
285-
* @param host Host to connect to. Used when connecting to different Snowflake deployments
284+
* @param host Host to connect to. Must be set using SF_CONNECT.host which has been set already before connection.
286285
* @param port Port to connect to. Used when connecting to a non-conventional port.
287286
* @param url URL path to use.
288287
* @param vars URL parameters to add to the encoded URL.
@@ -292,7 +291,7 @@ uint32 get_next_sleep_with_jitter(DECORRELATE_JITTER_BACKOFF *djb, uint32 sleep,
292291
* to XP resources)
293292
* @return Returns a pointer to a string which is the the encoded URL.
294293
*/
295-
char * STDCALL encode_url(CURL *curl, const char *protocol, const char *account, const char *host, const char *port,
294+
char * STDCALL encode_url(CURL *curl, const char *protocol, const char *host, const char *port,
296295
const char *url, URL_KEY_VALUE* vars, int num_args, SF_ERROR_STRUCT *error, char *extraUrlParams);
297296

298297
/**

scripts/build_curl.bat

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
@echo off
1313
set CURL_SRC_VERSION=8.7.1
14-
set CURL_BUILD_VERSION=4
14+
set CURL_BUILD_VERSION=6
1515
set CURL_VERSION=%CURL_SRC_VERSION%.%CURL_BUILD_VERSION%
1616
call %*
1717
goto :EOF

scripts/build_curl.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ function usage() {
1313
set -o pipefail
1414

1515
CURL_SRC_VERSION=8.7.1
16-
CURL_BUILD_VERSION=4
16+
CURL_BUILD_VERSION=6
1717
CURL_VERSION=${CURL_SRC_VERSION}.${CURL_BUILD_VERSION}
1818

1919
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

tests/test_unit_connect_parameters.c

+18
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,23 @@ void test_connection_parameters_with_region(void **unused) {
5252
SF_FREE(sf);
5353
}
5454

55+
/**
56+
* Test with cn region
57+
*/
58+
void test_connection_parameters_with_cn_region(void **unused) {
59+
SF_CONNECT *sf = (SF_CONNECT *) SF_CALLOC(1, sizeof(SF_CONNECT));
60+
sf->account = "testaccount";
61+
sf->user = "testuser";
62+
sf->password = "testpassword";
63+
sf->region = "cn-somewhere";
64+
assert_int_equal(
65+
_snowflake_check_connection_parameters(sf), SF_STATUS_SUCCESS);
66+
assert_string_equal(sf->host,
67+
"testaccount.cn-somewhere.snowflakecomputing.cn");
68+
69+
SF_FREE(sf);
70+
}
71+
5572
/**
5673
* Test account including region
5774
*/
@@ -208,6 +225,7 @@ int main(void) {
208225
cmocka_unit_test(test_connection_parameters_default_port),
209226
cmocka_unit_test(test_connection_parameters_no_host),
210227
cmocka_unit_test(test_connection_parameters_with_region),
228+
cmocka_unit_test(test_connection_parameters_with_cn_region),
211229
cmocka_unit_test(test_connection_parameters_including_region),
212230
cmocka_unit_test(test_connection_parameters_including_region_including_dot),
213231
cmocka_unit_test(test_connection_parameters_for_global_url_basic),

0 commit comments

Comments
 (0)