static SQLRETURN dbc_curl_init()

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;
}