Home > Software design >  linux openssl simple client non-blocking bio over already existing socket, buffer, and epoll abstrac
linux openssl simple client non-blocking bio over already existing socket, buffer, and epoll abstrac

Time:03-07

Is there simple example code that shows how to create a non-blocking network-based bio from scratch? I do not need to verify right now or renegotiate, just get data flowing back and forth first.

I'm trying to use openssl on top of an already existing abstraction of epoll, sockets, and buffers. I already have existing machinery for all of that and am trying to create a BIO over it, but I cannot for the life of me get it to work. I already have an established TCP connection, so I need to insert that into a source/sink bio and then do the handshake.

The current state is the dreaded scenario where SSL_connect returns -1, SSL_get_error returns 5 (syscall error), and errno reads SUCCESS. I have seen others have the same problem, but not a single answer.

The reason for doing this instead of just using a mem bio to shuttle bytes to back forth is because the rest of the stack is fairly well optimized, and I don't want to do the extra copying.

My first idea what just to implement a bio over the in and out buffers I already maintain, but I cannot get that work either. There are a lot questions floating around this site and others. Some have outdated answers, but most just don't have answers that work when they do have answers at all.

CodePudding user response:

Well, interesting. I would have made this a comment but it's too long.

I think this might boil down to the version of OpenSSL you are using. I took my existing blocking socket implementation and did this (sorry, it's a bit quick and dirty and doesn't bother to clear the error stack as it should, but that doesn't seem to be causing a problem. The busy loop is intentional, to hammer OpenSSL as hard as possible, but I also tried it with a short sleep and the result was the same):

u_long non_blocking = 1;
int ioctl_err = ioctlsocket (skt, FIONBIO, &non_blocking);
assert (ioctl_err == 0);
int connect_result = 0;
int connect_err = 0;

for ( ; ; )
{
    connect_result = SSL_connect (ssl);
    if (connect_result >= 0)
        break;

    connect_err = SSL_get_error (ssl, connect_result);
    if (connect_err != SSL_ERROR_WANT_READ && connect_err != SSL_ERROR_WANT_WRITE)
        break;                  // I put a breakpoint here; it was never hit
}

u_long non_blocking = 0;        // so that the rest of my code still works
ioctl_err = ioctlsocket (skt, FIONBIO, &non_blocking);

if (connect_result <= 0)        // this never happened either
    do_something_appropriate ();

// Various (synchronous) calls to `SSL_read` and `SSL_write`, these all worked fine

Now I know this isn't exactly what you are doing, but from OpenSSL's point of view that shouldn't matter. So, for me, I can get it to work.

Testing environment:

  • Windows (sorry!)
  • OpenSSL 3.0.1 (which, if not the latest, is not far off)

tl;dr If you're using the OpenSSL libraries that came with your Linux installation, it might be time to move on. Plus, if you build it from source you can build it with debug info, which might come in handy some time (I've actually built two versions for exactly this reason - one optimised and one not).

So that's it. HTH. Looks like you might be OK after all.


PS: I'm obviously not doing anything with BIOs here, and maybe your problem lies there instead. If so, we need some self-contained sample code using those which exhibits the problems you are having. Then, perhaps, someone can suggest a solution.

CodePudding user response:

Define BIO method:

  • BIO_meth_new
  • BIO_meth_set_write_ex
  • BIO_meth_set_read_ex
  • BIO_meth_set_ctrl
  • BIO_meth_free

Create BIO:

  • BIO_new
  • BIO_set_data
  • BIO_free

Associate BIO with SSL:

  • SSL_set_bio
  • Related