0

I want to send a request to an api using a socket.

I know the service is running as the legacy code in python works. See an excerpt below:

class SocketConnection:
    def __init__(self, payload, host):
        self.payload = payload
        self.host = host
        self.sock = None
        self.response_text = {}
        self.status_code = None
        self.full_resp = ""

    def connect(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        context = ssl.create_default_context()
        sock = context.wrap_socket(sock, server_hostname=self.host)
        sock.connect((self.host, 443))
        self.sock = sock

    def send(self):
        self.sock.send(self.payload)

    def receive(self):
        self.full_resp = self.sock.recv(4096).decode("utf-8")
        self.response_text = [self.full_resp]
        self.status_code = self.full_resp[9:12]
        self.sock.close()

I have the following C++ code which will always succeed in finding an address it can connect to, but can never find one that passes SSL_connect. SSL_get_error returns 1.

array<struct addrinfo*, 2> get_valid_addrinfo(string s) {
  struct addrinfo *res;
  struct addrinfo hints;
  memset(&hints, '\0', sizeof(hints));
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_family = AF_INET;
  hints.ai_flags = AI_ADDRCONFIG;
  int e = getaddrinfo(s.c_str(), "https", &hints, &res);
  if (e != 0) {
    printf("failure %s\n", gai_strerror (e));
    assert(-1);
  }
  int sock = -1;
  struct addrinfo *r ;
  SSL_CTX * ctx = make_ssl_context();
  SSL * ssl;
  for (r = res; r != NULL; r = r->ai_next) {
    sock = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
    if (sock != -1 && connect(sock, r->ai_addr, r->ai_addrlen) == 0) {
      free(ssl);
      ssl = SSL_new(ctx);
      SSL_set_fd(ssl, sock);
      if (SSL_connect(ssl) != -1)
        break;
    }
    if (sock != -1) {
       close(sock);
       sock = -1;
    }
  }
  assert(sock != -1);
  close(sock);
  free(ssl);
  SSL_CTX_free(ctx);
  return {res, r};
}

I'm on debian 10.

What am I doing wrong?

Edward
  • 468
  • 4
  • 18
  • 1
    Nothing is known about the requirements of the server - and there is no way to check because nothing is known about the server. Nothing is known about the exact error - please get this with [ERR_get_error](https://www.openssl.org/docs/manmaster/man3/ERR_get_error.html). A major difference between the C++ code and the Python code is that the latter uses SNI (`server_hostname=self.host`) and the C++ code not. Try to omit this option from Python and see if it still works. – Steffen Ullrich Mar 02 '22 at 18:29
  • @SteffenUllrich python just says the hostname is required if i ommit it or set it to an empty string. I've never heard of SNI. How can I use it in the cpp code? – Edward Mar 02 '22 at 18:38
  • 2
    *"I've never heard of SNI. How can I use it in the cpp code?"* - see [How to implement Server Name Indication (SNI)](https://stackoverflow.com/questions/5113333/how-to-implement-server-name-indication-sni). *"python just says the hostname is required"* - set `context.check_hostname=False`. – Steffen Ullrich Mar 02 '22 at 18:45
  • @SteffenUllrich I added SNI and it worked - if you write this as an answer i'll accept it. – Edward Mar 02 '22 at 20:29

1 Answers1

1

Before you try to fix your code, you should add some proper error reporting to it so that you're not shooting in the dark. When an OpenSSL call fails, you can do:

unsigned long e = ERR_get_error ();
char errbuf [120];             // magic number, documented!
ERR_error_string (e, errbuf);

And then report the error however you wish.

I find the string returned by ERR_error_string particularly useful, and in fact OpenSSL maintains something called an 'error stack', so you should loop on ERR_get_error until it returns zero (or, if you only want to report the first error, you can call ERR_clear_error instead, but it's important to clear the error stack one way or another before you move on). If you browse the docs, you can read up on all this stuff.

And I see that Steffen has just answered your other query, so I don't need to write anything more :) I do know however that cloud service providers like Cloudflare need this.

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
  • what would be a good resource to follow to learn more about openssl - the docs are very dense and not too easy to navigate around – Edward Mar 02 '22 at 20:53
  • 1
    Sorry, I can't help you there. I agree with you about the docs, but I managed to bluff my way through. SNI had me going for a while though. But do make sure to put proper error checking (and reporting) in. When Discogs moved to Cloudflare and broke my app, it saved my bacon. [pause]. A quick nose around found [this](https://opensource.com/article/19/6/cryptography-basics-openssl-part-1). It's mostly conceptual, but at least it's a start. – Paul Sanders Mar 02 '22 at 21:15