Skip to content

Commit

Permalink
unhardcode top level domain
Browse files Browse the repository at this point in the history
  • Loading branch information
sfc-gh-ext-simba-hx committed Jul 16, 2024
1 parent b19db15 commit edb4464
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 42 deletions.
71 changes: 60 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,9 @@
#include <direct.h>
#include <time.h>

#include <Shellapi.h>
#define strncasecmp _strnicmp

typedef HANDLE SF_THREAD_HANDLE;
typedef CONDITION_VARIABLE SF_CONDITION_HANDLE;
typedef CRITICAL_SECTION SF_CRITICAL_SECTION_HANDLE;
Expand Down Expand Up @@ -76,10 +79,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 +231,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 +2270,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 +2301,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 +2402,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
18 changes: 16 additions & 2 deletions lib/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
#include "authenticator.h"
#include "query_context_cache.h"

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

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

// Define internal constants
Expand Down Expand Up @@ -432,8 +437,17 @@ _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);
}
} else {
sf_sprintf(buf, sizeof(buf), "%s.snowflakecomputing.com",
sf->account);
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;
}

// 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=5
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=5
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
55 changes: 55 additions & 0 deletions tests/unit_test_ocsp/test_ocsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
#include <unistd.h>
#include <curl/curl.h>

extern CURLcode checkTelemetryHosts(char *hostname);

extern void get_cache_server_url(char* buf, size_t bufsize);

extern void get_cache_retry_url_pattern(char* buf, size_t bufsize);

extern CURLcode encodeUrlData(const char *url_data, size_t data_size, char** outptr, size_t *outlen);

struct configData
Expand Down Expand Up @@ -234,6 +240,47 @@ checkUrlEncoding(char *urlData, char *expectedEncoding)
fprintf(stderr, "OK\n");
}

static void
checkDefaultURLDomain(char *host, char *port, char *cacert)
{
// Max length of buffer
#define MAX_BUFFER_LENGTH 4096
char expectedCacheServerURL[MAX_BUFFER_LENGTH];
char expectedRetryURL[MAX_BUFFER_LENGTH];
char buf[MAX_BUFFER_LENGTH];

// unset environment variables to use default behavior
unsetenv("SF_OCSP_RESPONSE_CACHE_SERVER_ENABLED");
unsetenv("SF_OCSP_RESPONSE_CACHE_SERVER_URL");
// test default URL being used when ACTIVATE_SSD is on
setenv("SF_OCSP_ACTIVATE_SSD", "true", 1);

char * domain = strrchr(host, '.') + 1;
sprintf(expectedCacheServerURL, "http://ocsp.snowflakecomputing.%s/ocsp_response_cache.json", domain);
sprintf(expectedRetryURL, "http://ocsp.snowflakecomputing.%s/retry", domain);

checkCertificateRevocationStatus(host, port, cacert, NULL, NULL, 0, 0, 0);

get_cache_server_url(buf, sizeof(buf));
if (strcmp(buf, expectedCacheServerURL) != 0)
{
fprintf(stderr, "checkDefaultURLDomain FAILED! expected cache server URL %s but actually %s\n",
expectedCacheServerURL, buf);
exit(1);
}

get_cache_retry_url_pattern(buf, sizeof(buf));
if (strcmp(buf, expectedRetryURL) != 0)
{
fprintf(stderr, "checkDefaultURLDomain FAILED! expected retry URL %s but actually %s\n",
expectedRetryURL, buf);
exit(1);
}

unsetenv("SF_OCSP_ACTIVATE_SSD");
fprintf(stderr, "OK\n");
}

int main(int argc, char **argv)
{
char cacert[4096];
Expand Down Expand Up @@ -378,5 +425,13 @@ int main(int argc, char **argv)
unsetenv("SF_OCSP_RESPONSE_CACHE_SERVER_ENABLED");
unsetenv("SF_OCSP_RESPONSE_CACHE_SERVER_URL");

printf("===> Case 12: Ignore top level domain when checking telemetry endpoints\n");
dieIfNotSuccess(checkTelemetryHosts("sfctest.client-telemetry.snowflakecomputing.com"));
dieIfNotSuccess(checkTelemetryHosts("sfcdev.client-telemetry.snowflakecomputing.cn"));
dieIfNotSuccess(checkTelemetryHosts("client-telemetry.snowflakecomputing.mil"));
fprintf(stderr, "OK\n");

printf("===> Case 13: Top level domain used in default OCSP URLs\n");
checkDefaultURLDomain(host, port, cacert);
return 0;
}

0 comments on commit edb4464

Please sign in to comment.