Skip to content

Instantly share code, notes, and snippets.

@lampmanyao
Created June 1, 2021 10:20
Show Gist options
  • Save lampmanyao/4bdd2da94988370bfe024c3158356b51 to your computer and use it in GitHub Desktop.
Save lampmanyao/4bdd2da94988370bfe024c3158356b51 to your computer and use it in GitHub Desktop.
non-blocking BIO_do_connect()
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* strerror */
#include <errno.h>
#include <poll.h>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
static void print_ssl_err(unsigned long ssl_err, const char * const func)
{
const char * const err_str = ERR_reason_error_string(ssl_err);
if (err_str)
fprintf(stderr, "%s\n", err_str);
else
fprintf(stderr, "%s failed: %lu (0x%lx)\n", func, ssl_err, ssl_err);
}
/*
* Compile:
* macOS:
* gcc -o a.out nb_bio_conn.c -I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib -lssl -lcrypto
*/
int main(int argc, char **argv)
{
if (argc != 3) {
fprintf(stderr, "%s host port\n", argv[0]);
exit(EXIT_FAILURE);
}
const char *host = argv[1];
int port = atoi(argv[2]);
if (errno != 0) {
fprintf(stderr, "atoi(): %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
(void)SSL_library_init();
SSL_load_error_strings();
SSL_CTX *ctx = NULL;
SSL *ssl = NULL;
BIO *bio = NULL;
long ret;
unsigned long ssl_err;
char hostname[4096];
const SSL_METHOD *method = TLS_client_method();
if (!method) {
ssl_err = ERR_get_error();
print_ssl_err(ssl_err, "TLS_client_method");
goto err;
}
ctx = SSL_CTX_new(TLS_client_method());
if (!ctx) {
ssl_err = ERR_get_error();
print_ssl_err(ssl_err, "SSL_CTX_new");
goto err;
}
bio = BIO_new_ssl_connect(ctx);
if (!bio) {
ssl_err = ERR_get_error();
print_ssl_err(ssl_err, "BIO_new_ssl_connect");
goto err;
}
snprintf(hostname, sizeof(hostname), "%s:%d", host, port);
ret = BIO_set_conn_hostname(bio, hostname);
if (ret != 1) {
ssl_err = ERR_get_error();
print_ssl_err(ssl_err, "BIO_set_conn_hostname");
goto err;
}
BIO_get_ssl(bio, &ssl);
SSL_set_tlsext_host_name(ssl, host);
BIO_set_nbio(bio, 1);
while (1) {
ret = BIO_do_connect(bio);
if (ret == 1) {
break;
}
int n;
struct pollfd pollfd;
int ms = 1000; /* timeout */
int err = SSL_get_error(ssl, (int)ret);
switch (err) {
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_WANT_READ:
pollfd.fd = SSL_get_fd(ssl);
pollfd.events = POLLIN | POLLOUT;
n = poll(&pollfd, 1, ms);
if (n == 1) {
continue;
} else {
fprintf(stdout, "connect to %s:%d timeouted: %d ms\n",
host, port, ms);
goto err;
}
case SSL_ERROR_WANT_CONNECT:
continue;
default:
ssl_err = ERR_get_error();
print_ssl_err(ssl_err, "BIO_do_connect");
break;
}
}
if (ret != 1) {
fprintf(stderr, "connect to %s:%d failed\n", host, port);
goto err;
}
fprintf(stdout, "connect to %s:%d done\n", host, port);
ret = BIO_do_handshake(bio);
if (ret != 1) {
ssl_err = ERR_get_error();
print_ssl_err(ssl_err, "BIO_do_handshake");
goto err;
}
fprintf(stdout, "tls handshake done\n");
/* do the read and write things */
err:
if (ctx) SSL_CTX_free(ctx);
if (bio) BIO_free(bio);
exit(EXIT_SUCCESS);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment