in driver/connect.c [475:735]
static SQLRETURN dbc_curl_init(esodbc_dbc_st *dbc)
{
CURL *curl;
SQLRETURN ret;
BOOL compress;
char apikey_buff[APIKEY_BUFF_SIZE], *apikey_ptr = NULL;
struct curl_slist *curl_hdrs;
assert(! dbc->curl);
/* get a libcurl handle */
curl = curl_easy_init();
if (! curl) {
ERRNH(dbc, "libcurl: failed to fetch new handle.");
RET_HDIAG(dbc, SQL_STATE_HY000, "failed to init the transport", 0);
} else {
dbc->curl = curl;
}
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER,
dbc->curl_err_buff);
dbc->curl_err_buff[0] = '\0';
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to set error buffer.");
goto err;
}
/* set the behavior for redirection */
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION,
dbc->follow);
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to set redirection behavior.");
goto err;
}
/* set the accepted encoding (compression) header */
compress = dbc->compression == ESODBC_CMPSS_ON ||
(dbc->compression == ESODBC_CMPSS_AUTO && !dbc->secure);
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING,
compress ? "" : NULL);
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to set HTTP headers list.");
goto err;
}
if (dbc->secure) {
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER,
ESODBC_SEC_CHECK_CA <= dbc->secure ? 1L : 0L);
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to enable CA check.");
goto err;
}
if (ESODBC_SEC_CHECK_CA <= dbc->secure) {
/* set path to CA */
if (dbc->ca_path.cnt) {
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_CAINFO,
dbc->ca_path.str);
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to set CA path.");
goto err;
}
}
/* verify host name */
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST,
ESODBC_SEC_CHECK_HOST <= dbc->secure ? 2L : 0L);
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to enable host check.");
goto err;
}
/* verify the revocation chain? */
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS,
ESODBC_SEC_CHECK_REVOKE <= dbc->secure ?
0L : CURLSSLOPT_NO_REVOKE);
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to enable host check.");
goto err;
}
}
/*
* TODO expose: CURLOPT_SSLVERSION, CURLOPT_SSLCERTTYPE
* CURLOPT_SSL_ENABLE_ALPN, CURLOPT_SSL_ENABLE_NPN,
* (CURLOPT_SSL_FALSESTART), CURLOPT_SSL_VERIFYSTATUS,
* CURLOPT_PROXY_*
*/
/* TLS has its own compression options (RFC3749), so doing it twice
* will likelyl be detrimental, but there's also the security
* implication (CVE-2012-4929). */
if (compress) {
WARNH(dbc, "compression and encryption are both enabled.");
}
}
/* set authentication parameters */
if (dbc->uid.cnt) {
/* set the authentication methods:
* "basic" is currently - 7.0.0 - the only supported method */
/* Note: libcurl (7.61.0) won't pick Basic auth over SSL with _ANY */
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_HTTPAUTH,
CURLAUTH_BASIC);
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to set HTTP auth methods.");
goto err;
}
/* set the username */
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_USERNAME, dbc->uid.str);
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to set auth username.");
goto err;
}
/* set the password */
if (dbc->pwd.cnt) {
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_PASSWORD,
dbc->pwd.str);
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to set auth password.");
goto err;
}
} else {
/* not an error per se, but make it always visible */
ERRH(dbc, "no password provided for username `" LCPDL "`! "
"Intended?", LCSTR(&dbc->uid));
}
if (dbc->follow) {
/* restrict sharing credentials to first contacted host? */
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH,
/* if not secure, "make it work" */
dbc->secure ? 0L : 1L);
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to set unrestricted auth.");
goto err;
}
}
} else if (dbc->api_key.cnt) {
if (sizeof(HTTP_AUTH_API_KEY) + dbc->api_key.cnt <
sizeof(apikey_buff)) {
apikey_ptr = apikey_buff;
} else {
DBGH(dbc, "static buffer size %zuB less than required %zuB, "
"allocating.", sizeof(apikey_buff), sizeof(HTTP_AUTH_API_KEY) +
dbc->api_key.cnt);
apikey_ptr = malloc(sizeof(HTTP_AUTH_API_KEY) + dbc->api_key.cnt);
if (! apikey_ptr) {
ERRNH(dbc, "OOM for %zuB.", sizeof(HTTP_AUTH_API_KEY) +
dbc->api_key.cnt);
goto err;
}
}
memcpy(apikey_ptr, HTTP_AUTH_API_KEY, sizeof(HTTP_AUTH_API_KEY) - 1);
memcpy(apikey_ptr + sizeof(HTTP_AUTH_API_KEY) - 1, dbc->api_key.str,
dbc->api_key.cnt);
apikey_ptr[sizeof(HTTP_AUTH_API_KEY) - 1 + dbc->api_key.cnt] = '\0';
dbc->curl_hdrs = curl_slist_append(NULL, apikey_ptr);
if (apikey_ptr != apikey_buff) {
free(apikey_ptr);
apikey_ptr = NULL;
}
if (! dbc->curl_hdrs) {
ERRH(dbc, "libcurl: failed to init API key Auth header.");
goto err;
}
} else {
INFOH(dbc, "no username or API key provided: auth disabled.");
}
/* set the HTTP headers: Content-Type, Accept, Authorization(?) */
curl_hdrs = dbc->pack_json ? json_headers : cbor_headers;
/* is there already an Autorization header set? then chain the pack hdrs */
if (dbc->curl_hdrs) {
if (! (curl_hdrs = curl_slist_duplicate(curl_hdrs))) {
ERRNH(dbc, "failed duplicating packing format headers.");
goto err;
}
dbc->curl_hdrs->next = curl_hdrs;
/* if there's no Authz header, the pack headers won't be dup'd */
curl_hdrs = dbc->curl_hdrs;
}
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_hdrs);
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to set HTTP headers list.");
goto err;
}
/* proxy parameters */
if (dbc->proxy_url.cnt) {
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_PROXY,
dbc->proxy_url.str);
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to set the proxy URL.");
goto err;
}
if (dbc->proxy_uid.cnt) {
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME,
dbc->proxy_uid.str);
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to set the proxy username.");
goto err;
}
if (dbc->proxy_pwd.cnt) {
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_PROXYPASSWORD,
dbc->proxy_pwd.str);
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to set the proxy password.");
goto err;
}
}
}
} else {
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_PROXY, "");
if (dbc->curl_err != CURLE_OK) {
WARNH(dbc, "libcurl: failed to generally disable proxying.");
}
}
/* set the write call-back for answers */
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
write_callback);
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to set write callback.");
goto err;
}
/* ... and its argument */
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_WRITEDATA, dbc);
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to set callback argument.");
goto err;
}
#ifndef NDEBUG
if (dbc->hdr.log && LOG_LEVEL_DBG <= dbc->hdr.log->level) {
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION,
debug_callback);
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to set debug callback.");
goto err;
}
/* ... and its argument */
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_DEBUGDATA, dbc);
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to set dbg callback argument.");
goto err;
}
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to activate verbose mode.");
goto err;
}
}
#endif /* NDEBUG */
DBGH(dbc, "libcurl: new handle 0x%p.", curl);
return SQL_SUCCESS;
err:
ret = dbc_curl_post_diag(dbc, SQL_STATE_HY000);
cleanup_curl(dbc);
return ret;
}