1

I have libcurl built with OpenSSL backend. I want to set SNI to some specified string. the way that I could find is using the function SSL_set_tlsext_host_name which takes the SSL * instance and a string and then sets it. (see https://stackoverflow.com/a/5113466/3754125)

However curl_easy does not have a call back to retrieve SSL* instance. Is there an alternate way to do so?

Some more context: In my environment, I have to use CURLOPT_RESOLVE to resolve the FQDN to IPv4. There is the FQDN: const char *fqdn IPv4 which fqdn should resolve to: uint32_t ipv4 fake SNI: const char *sni The gist looks something like:

CURL *ez;
char buf[ENOUGH];
struct curl_slist *resolver;
/* ... */
snprintf(buf, sizeof(buf), "%s:%d:%d.%d.%d.%d", fqdn, port, IP(IPv4));
resolver = curl_slist_append(NULL, buf);
curl_easy_setopt(ez, CURLOPT_RESOLVE, resolver);

After this I need to set the SNI to the fake SNI without touching the resolver.

milaniez
  • 1,051
  • 1
  • 9
  • 21

1 Answers1

1

If you want to "fake" the SNI then CURLOPT_RESOLVE or CURLOPT_CONNECT_TO are available options to reach the same end goal.

CURLOPT_RESOLVE example

Run a HTTPS server on 127.0.0.1 but make curl think it is example.com when it connects to it (so it sends that as SNI and in the Host: header)

CURL *curl;
struct curl_slist *host = NULL;
host = curl_slist_append(NULL, "example.com:443:127.0.0.1");

curl = curl_easy_init();
if(curl) {
  curl_easy_setopt(curl, CURLOPT_RESOLVE, host);
  curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");

  curl_easy_perform(curl);

  /* always cleanup */
  curl_easy_cleanup(curl);
}

curl_slist_free_all(host);

CURLOPT_CONNECT_TO example

Run a dev HTTPS server on the host name server1.example.com but you want curl to connect to it thinking it is the www.example.org server.

CURL *curl;
struct curl_slist *connect_to = NULL;
connect_to = curl_slist_append(NULL, "www.example.org::server1.example.com:");

curl = curl_easy_init();
if(curl) {
  curl_easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to);
  curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.org");

  curl_easy_perform(curl);

  /* always cleanup */
  curl_easy_cleanup(curl);
}

curl_slist_free_all(connect_to);
Daniel Stenberg
  • 54,736
  • 17
  • 146
  • 222
  • How would one precisely do it with `CURLOPT_RESOLVE` or `CURLOPT_CONNECT_TO`? I am already using `CURLOPT_RESOLVE` to resolve FQDN to IPv4. – milaniez Jun 05 '19 at 17:28
  • Thanks for including the code in your answer. However, this does not work for me. I am using `CURLOPT_RESOLVE` to resolve FQDN to IPv4 (looks like `curl_slist_append(NULL, "www.example.org:10443:172.18.27.161")`) (I have to do this because I have a specific API to resolve FQDN to IPv4 otherwise curl cannot resolve). The string that I am trying to inject into SNI is actually a token which the server uses to identify how it should dispatch the connection. The token looks like something like this: `tYqxjS6ntrqmPtBwUli1`. So If I want to fake SNI with `CURLOPT_RESOLVE` I cannot resolve the fqdn! – milaniez Jun 05 '19 at 22:01
  • Okay, but then you're not doing HTTPS anymore... nothing wrong in that, but curl is focused on talking the specific protocols we've made it support. – Daniel Stenberg Jun 06 '19 at 09:16
  • Actually I am doing https! It is something like `curl_easy_setopt(ez_h, CURLOPT_URL, "https://www.example.org:10443/api/entry/point")`. Are you implying that I cannot fake SNI in https? – milaniez Jun 06 '19 at 17:44
  • On second thought on this solution, it worked for my case. `ipv4=getipv4(fqdn); snprintf(resolve_entry, sizeof(resolve_entry), "%s:%s:%d.%d.%d.%d", fake_sni, port, IP(ipv4)); resolve_list = curl_slist_add(NULL, resolve_entry);snprintf(url, sizeof(url), "https://%s:%s/%s", fake_sni, port, api_entry_point);` The only catch is that I had to set the referer to `fqdn`. – milaniez Jun 11 '19 at 17:26