Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SNOW-1526511: unhardcode top level domain #732

Merged
merged 5 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 61 additions & 11 deletions deps/curl/lib/vtls/sf_ocsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@
#include <direct.h>
#include <time.h>

#include <Shellapi.h>
#define strncasecmp _strnicmp
#define strcasecmp _stricmp

typedef HANDLE SF_THREAD_HANDLE;
typedef CONDITION_VARIABLE SF_CONDITION_HANDLE;
typedef CRITICAL_SECTION SF_CRITICAL_SECTION_HANDLE;
Expand Down Expand Up @@ -76,10 +80,11 @@ typedef pthread_mutex_t SF_MUTEX_HANDLE;
#define strcasecmp _stricmp
#endif

#define DEFAULT_OCSP_RESPONSE_CACHE_HOST "http://ocsp.snowflakecomputing.com"
#define DEFAULT_OCSP_RESPONSE_CACHE_HOST "http://ocsp.snowflakecomputing.%s"
#define OCSP_RESPONSE_CACHE_JSON "ocsp_response_cache.json"
#define OCSP_RESPONSE_CACHE_URL "%s/%s"
#define OCSP_RESPONDER_RETRY_URL "http://ocsp.snowflakecomputing.com/retry"
#define OCSP_RESPONDER_RETRY_URL "http://ocsp.snowflakecomputing.%s/retry"
#define MAX_DOMAIN_LEN 64 //max 63 characters + terminator

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

static char ocsp_cache_server_retry_url_pattern[MAX_BUFFER_LENGTH];

static char default_ocsp_cache_host[sizeof(DEFAULT_OCSP_RESPONSE_CACHE_HOST) + MAX_DOMAIN_LEN] = "";

static char default_ocsp_cache_retry_url[sizeof(OCSP_RESPONDER_RETRY_URL) + MAX_DOMAIN_LEN] = "";

// functions for test purpose only
SF_PUBLIC(CURLcode) checkTelemetryHosts(char *hostname)
{
struct connectdata conn;
conn.host.name = hostname;
return checkCertOCSP(&conn, NULL, NULL, NULL, 0, 0);
}

void get_cache_server_url(char* buf, size_t bufsize)
{
strncpy(buf, ocsp_cache_server_url, bufsize);
}

void get_cache_retry_url_pattern(char* buf, size_t bufsize)
{
strncpy(buf, ocsp_cache_server_retry_url_pattern, bufsize);
}

/* Mutex */
int _mutex_init(SF_MUTEX_HANDLE *lock) {
#ifdef _WIN32
Expand Down Expand Up @@ -2244,10 +2271,24 @@ void initOCSPCacheServer(struct Curl_easy *data)

if (ocsp_cache_server_url_env == NULL)
{
char* top_domain = strrchr(data->conn->host.name, '.');
if (top_domain)
{
top_domain++;
}
else
{
// It's basically impossible not finding top domain in host.
// Use "com" as default just in case.
top_domain = "com";
}

/* default URL */
snprintf(default_ocsp_cache_host, sizeof(default_ocsp_cache_host),
DEFAULT_OCSP_RESPONSE_CACHE_HOST, top_domain);
snprintf(ocsp_cache_server_url, sizeof(ocsp_cache_server_url),
OCSP_RESPONSE_CACHE_URL,
DEFAULT_OCSP_RESPONSE_CACHE_HOST,
default_ocsp_cache_host,
OCSP_RESPONSE_CACHE_JSON);

if (!ACTIVATE_SSD)
Expand All @@ -2261,9 +2302,11 @@ void initOCSPCacheServer(struct Curl_easy *data)
* Non private link customers always go to default
* retry URL for OCSP retries
*/
snprintf(default_ocsp_cache_retry_url, sizeof(default_ocsp_cache_retry_url),
OCSP_RESPONDER_RETRY_URL, top_domain);
strncpy(ocsp_cache_server_retry_url_pattern,
OCSP_RESPONDER_RETRY_URL,
sizeof(OCSP_RESPONDER_RETRY_URL));
default_ocsp_cache_retry_url,
strlen(default_ocsp_cache_retry_url) + 1);
}
}
else
Expand Down Expand Up @@ -2360,15 +2403,22 @@ SF_PUBLIC(CURLcode) checkCertOCSP(struct connectdata *conn,
SF_FAILOPEN_STATUS ocsp_fail_open = ENABLED;
char last_timeout_host[MAX_BUFFER_LENGTH];
last_timeout_host[0] = '\0';

// SNOW-1526511 ignore top level domain name to be more flexible
const char* telemetry_endpoints[] = {
"sfctest.client-telemetry.snowflakecomputing.",
"sfcdev.client-telemetry.snowflakecomputing.",
"client-telemetry.snowflakecomputing."
};
const int telemetry_endpoints_num = sizeof(telemetry_endpoints) / sizeof(char*);

// These end points are Out of band telemetry end points.
// Do not use OCSP/failsafe on Out of band telemetry endpoints
if ( (strcmp(conn->host.name, "sfctest.client-telemetry.snowflakecomputing.com") == 0 )
|| (strcmp(conn->host.name, "sfcdev.client-telemetry.snowflakecomputing.com") == 0 )
|| (strcmp(conn->host.name, "client-telemetry.snowflakecomputing.com") == 0 )
) {
return rs;
for (int i = 0; i < telemetry_endpoints_num; i++)
{
if (strncasecmp(conn->host.name, telemetry_endpoints[i], strlen(telemetry_endpoints[i])) == 0)
{
return rs;
}
}

SF_OTD ocsp_log_data;
Expand Down
34 changes: 32 additions & 2 deletions lib/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
#include "authenticator.h"
#include "query_context_cache.h"

#ifdef _WIN32
#include <Shellapi.h>
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#endif

#define curl_easier_escape(curl, string) curl_easy_escape(curl, string, 0)

// Define internal constants
Expand Down Expand Up @@ -432,14 +438,38 @@ _snowflake_check_connection_parameters(SF_CONNECT *sf) {
// construct a host parameter if not specified,
char buf[1024];
if (sf->region) {
sf_sprintf(buf, sizeof(buf), "%s.%s.snowflakecomputing.com",
sf->account, sf->region);
if (strncasecmp(sf->region, "cn-", 3) == 0)
{
//region started with "cn-", use "cn" for top domain
sf_sprintf(buf, sizeof(buf), "%s.%s.snowflakecomputing.cn",
sf->account, sf->region);
}
else
{
sf_sprintf(buf, sizeof(buf), "%s.%s.snowflakecomputing.com",
sf->account, sf->region);
}
sfc-gh-dprzybysz marked this conversation as resolved.
Show resolved Hide resolved
} else {
sf_sprintf(buf, sizeof(buf), "%s.snowflakecomputing.com",
sf->account);
}
alloc_buffer_and_copy(&sf->host, buf);
}

char* top_domain = strrchr(sf->host, '.');
if (top_domain)
{
top_domain++;
}
else
{
// It's basically impossible not finding top domain in host.
// Log the entire host just in case.
top_domain = sf->host;
}

log_info("Connecting to %s Snowflake domain", (strcasecmp(top_domain, "cn") == 0) ? "CHINA" : "GLOBAL");

// split account and region if connected by a dot.
char *dot_ptr = strchr(sf->account, (int) '.');
if (dot_ptr) {
Expand Down
1 change: 0 additions & 1 deletion lib/client_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
#define HEADER_CLIENT_APP_ID_FORMAT "CLIENT_APP_ID: %s"
#define HEADER_CLIENT_APP_VERSION_FORMAT "CLIENT_APP_VERSION: %s"

#define DEFAULT_SNOWFLAKE_BASE_URL "snowflakecomputing.com"
#define DEFAULT_SNOWFLAKE_REQUEST_TIMEOUT 60

#define SESSION_URL "/session/v1/login-request"
Expand Down
40 changes: 17 additions & 23 deletions lib/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,6 @@ get_next_sleep_with_jitter(DECORRELATE_JITTER_BACKOFF *djb, uint32 sleep, uint64

char * STDCALL encode_url(CURL *curl,
const char *protocol,
const char *account,
const char *host,
const char *port,
const char *url,
Expand Down Expand Up @@ -594,34 +593,29 @@ char * STDCALL encode_url(CURL *curl,
const char *amp = "&";
size_t amp_size = strlen(amp);

if (host_empty)
{
SET_SNOWFLAKE_ERROR(error, SF_STATUS_ERROR_BAD_CONNECTION_PARAMS,
"Invalid host trying to create encoded url",
SF_SQLSTATE_UNABLE_TO_CONNECT);
goto cleanup;
}
sfc-gh-dstempniak marked this conversation as resolved.
Show resolved Hide resolved

// Set proper format based on variables passed into encode URL.
// The format includes format specifiers that will be consumed by empty fields
// (i.e if port is empty, add an extra specifier so that we have 1 call to snprintf, vs. 4 different calls)
// Format specifier order is protocol, then account, then host, then port, then url.
// Format specifier order is protocol, then then host, then port, then url.
// Base size increases reflect the number of static characters in the format string (i.e. ':', '/', '.')
if (!port_empty && !host_empty) {
format = "%s://%s%s:%s%s";
if (!port_empty) {
format = "%s://%s:%s%s";
base_url_size += 4;
// Set account to an empty string since host overwrites account
account = "";
} else if (port_empty && !host_empty) {
format = "%s://%s%s%s%s";
base_url_size += 3;
port = "";
// Set account to an empty string since host overwrites account
account = "";
} else if (!port_empty && host_empty) {
format = "%s://%s.%s:%s%s";
base_url_size += 5;
host = DEFAULT_SNOWFLAKE_BASE_URL;
} else {
format = "%s://%s.%s%s%s";
base_url_size += 4;
host = DEFAULT_SNOWFLAKE_BASE_URL;
format = "%s://%s%s%s";
base_url_size += 3;
port = "";
}
base_url_size +=
strlen(protocol) + strlen(account) + strlen(host) + strlen(port) +
strlen(protocol) + strlen(host) + strlen(port) +
strlen(url) + strlen(URL_QUERY_DELIMITER);

encoded_url_size = base_url_size;
Expand Down Expand Up @@ -660,7 +654,7 @@ char * STDCALL encode_url(CURL *curl,
SF_SQLSTATE_UNABLE_TO_CONNECT);
goto cleanup;
}
sf_sprintf(encoded_url, base_url_size, format, protocol, account, host, port,
sf_sprintf(encoded_url, base_url_size, format, protocol, host, port,
url);

// Initially add the query delimiter "?"
Expand Down Expand Up @@ -931,7 +925,7 @@ sf_bool STDCALL request(SF_CONNECT *sf,
}
}

encoded_url = encode_url(curl, sf->protocol, sf->account, sf->host,
encoded_url = encode_url(curl, sf->protocol, sf->host,
sf->port, url, url_params, num_url_params,
error, sf->directURL_param);
if (encoded_url == NULL) {
Expand Down Expand Up @@ -1015,7 +1009,7 @@ sf_bool STDCALL renew_session(CURL *curl, SF_CONNECT *sf, SF_ERROR_STRUCT *error
// Create request id, set in url parameter and encode url
uuid4_generate(request_id);
url_params[0].value = request_id;
encoded_url = encode_url(curl, sf->protocol, sf->account, sf->host,
encoded_url = encode_url(curl, sf->protocol, sf->host,
sf->port, RENEW_SESSION_URL, url_params, 1, error,
sf->directURL_param);
if (!encoded_url) {
Expand Down
5 changes: 2 additions & 3 deletions lib/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,7 @@ uint32 get_next_sleep_with_jitter(DECORRELATE_JITTER_BACKOFF *djb, uint32 sleep,
*
* @param curl cURL object for the request.
* @param protocol Protocol to use in the request. Either HTTP or HTTPS.
* @param account Snowflake account name. This should be the account of the user.
* @param host Host to connect to. Used when connecting to different Snowflake deployments
* @param host Host to connect to. Must be set using SF_CONNECT.host which has been set already before connection.
* @param port Port to connect to. Used when connecting to a non-conventional port.
* @param url URL path to use.
* @param vars URL parameters to add to the encoded URL.
Expand All @@ -292,7 +291,7 @@ uint32 get_next_sleep_with_jitter(DECORRELATE_JITTER_BACKOFF *djb, uint32 sleep,
* to XP resources)
* @return Returns a pointer to a string which is the the encoded URL.
*/
char * STDCALL encode_url(CURL *curl, const char *protocol, const char *account, const char *host, const char *port,
char * STDCALL encode_url(CURL *curl, const char *protocol, const char *host, const char *port,
const char *url, URL_KEY_VALUE* vars, int num_args, SF_ERROR_STRUCT *error, char *extraUrlParams);

/**
Expand Down
2 changes: 1 addition & 1 deletion scripts/build_curl.bat
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

@echo off
set CURL_SRC_VERSION=8.7.1
set CURL_BUILD_VERSION=4
set CURL_BUILD_VERSION=6
set CURL_VERSION=%CURL_SRC_VERSION%.%CURL_BUILD_VERSION%
call %*
goto :EOF
Expand Down
2 changes: 1 addition & 1 deletion scripts/build_curl.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function usage() {
set -o pipefail

CURL_SRC_VERSION=8.7.1
CURL_BUILD_VERSION=4
CURL_BUILD_VERSION=6
CURL_VERSION=${CURL_SRC_VERSION}.${CURL_BUILD_VERSION}

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
Expand Down
18 changes: 18 additions & 0 deletions tests/test_unit_connect_parameters.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,23 @@ void test_connection_parameters_with_region(void **unused) {
SF_FREE(sf);
}

/**
* Test with cn region
*/
void test_connection_parameters_with_cn_region(void **unused) {
SF_CONNECT *sf = (SF_CONNECT *) SF_CALLOC(1, sizeof(SF_CONNECT));
sf->account = "testaccount";
sf->user = "testuser";
sf->password = "testpassword";
sf->region = "cn-somewhere";
assert_int_equal(
_snowflake_check_connection_parameters(sf), SF_STATUS_SUCCESS);
assert_string_equal(sf->host,
"testaccount.cn-somewhere.snowflakecomputing.cn");

SF_FREE(sf);
}

/**
* Test account including region
*/
Expand Down Expand Up @@ -208,6 +225,7 @@ int main(void) {
cmocka_unit_test(test_connection_parameters_default_port),
cmocka_unit_test(test_connection_parameters_no_host),
cmocka_unit_test(test_connection_parameters_with_region),
cmocka_unit_test(test_connection_parameters_with_cn_region),
cmocka_unit_test(test_connection_parameters_including_region),
cmocka_unit_test(test_connection_parameters_including_region_including_dot),
cmocka_unit_test(test_connection_parameters_for_global_url_basic),
Expand Down
Loading
Loading