Skip to content

Instantly share code, notes, and snippets.

@roxlu
Created October 31, 2012 18:54
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save roxlu/3989091 to your computer and use it in GitHub Desktop.
Save roxlu/3989091 to your computer and use it in GitHub Desktop.
Testing with libuv, openssl, memory bios, non blocking sockets: how to handle ssl and application data (?)
#include <iostream>
#include <openssl/ssl.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <uv.h>
#include <vector>
#include <iterator>
#include <algorithm>
#define WHERE_INFO(ssl, w, flag, msg) { \
if(w & flag) { \
printf("\t"); \
printf(msg); \
printf(" - %s ", SSL_state_string(ssl)); \
printf(" - %s ", SSL_state_string_long(ssl)); \
printf("\n"); \
}\
}
// INFO CALLBACK
void dummy_ssl_info_callback(const SSL* ssl, int where, int ret) {
if(ret == 0) {
printf("ssl error occured.\n");
return;
}
WHERE_INFO(ssl, where, SSL_CB_LOOP, "LOOP");
WHERE_INFO(ssl, where, SSL_CB_EXIT, "EXIT");
WHERE_INFO(ssl, where, SSL_CB_READ, "READ");
WHERE_INFO(ssl, where, SSL_CB_WRITE, "WRITE");
WHERE_INFO(ssl, where, SSL_CB_ALERT, "ALERT");
WHERE_INFO(ssl, where, SSL_CB_HANDSHAKE_DONE, "HANDSHAKE DONE");
}
// MSG CALLBACK
void dummy_ssl_msg_callback(
int writep
,int version
,int contentType
,const void* buf
,size_t len
,SSL* ssl
,void *arg
)
{
printf("\tMessage callback with length: %zu\n", len);
}
// VERIFY
int dummy_ssl_verify_callback(int ok, X509_STORE_CTX* store) {
char buf[256];
X509* err_cert;
err_cert = X509_STORE_CTX_get_current_cert(store);
int err = X509_STORE_CTX_get_error(store);
int depth = X509_STORE_CTX_get_error_depth(store);
X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
printf("\tssl_verify_callback(), ok: %d, error: %d, depth: %d, name: %s\n", ok, err, depth, buf);
return 1;
}
// LIBUV
// ------
void on_written_callback(uv_write_t* req, int status);
enum HTTPStates {
HS_NONE
,HS_REQUEST_DONE
};
struct Client {
void addAppData(const std::string str) {
std::copy(str.begin(), str.end(), std::back_inserter(buffer_out));
}
uv_loop_t* loop;
uv_tcp_t socket;
uv_write_t write_req;
uv_connect_t connect_req;
char host[1024];
char port[1024];
std::vector<char> buffer_in; // app data in
std::vector<char> buffer_out; // app data out
SSL_CTX* ssl_ctx;
SSL* ssl;
BIO* read_bio;
BIO* write_bio;
int http_state;
};
void write_to_socket(Client* c, char* buf, size_t len) {
if(len <= 0) {
return;
}
uv_buf_t uvbuf;
uvbuf.base = buf;
uvbuf.len = len;
int r = uv_write(&c->write_req, (uv_stream_t*)&c->socket, &uvbuf, 1, on_written_callback);
if(r < 0) {
printf("ERROR: write_to_socket error: %s\n", uv_err_name(uv_last_error(c->socket.loop)));
}
}
void on_event(Client* c) {
char tmp_buf[10240];
int bytes_read;
size_t bytes_in_read_bio = BIO_ctrl_pending(c->read_bio);
size_t bytes_in_write_bio = BIO_ctrl_pending(c->write_bio);
int b = 0;
if(bytes_in_read_bio > 0) {
int r = SSL_write(c->ssl, tmp_buf, sizeof(tmp_buf));
b = 1;
if(r > 0) {
}
else {
int err = SSL_get_error(c->ssl, r);
if(err == SSL_ERROR_ZERO_RETURN) {
}
else if(err == SSL_ERROR_WANT_READ) {
}
else if(err == SSL_ERROR_WANT_WRITE) {
}
else {
}
}
}
// write to socket
int r = SSL_is_init_finished(c->ssl);
if(r == 0) {
while((bytes_read = BIO_read(c->write_bio, tmp_buf, sizeof(tmp_buf))) > 0) {
write_to_socket(c, tmp_buf, bytes_read);
}
}
{
int r = SSL_read(c->ssl, tmp_buf, sizeof(tmp_buf));
}
}
uv_buf_t on_alloc_callback(uv_handle_t* con, size_t size) {
uv_buf_t buf;
buf.base = (char*)malloc(size);
buf.len = size;
return buf;
}
void on_written_callback(uv_write_t* req, int status) {
}
void on_read_callback(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) {
Client* c = static_cast<Client*>(tcp->data);
if(nread == -1) {
printf("ERROR: on_read_callback error: %s\n", uv_err_name(uv_last_error(c->loop)));
::exit(0);
}
std::copy(buf.base, buf.base+nread, std::back_inserter(c->buffer_in));
int written = BIO_write(c->read_bio, buf.base, nread);
on_event(c);
}
void on_connect_callback(uv_connect_t* con, int status) {
Client* c = static_cast<Client*>(con->data);
if(status == -1) {
printf("ERROR: on_connect_callback %s\n", uv_err_name(uv_last_error(c->loop)));
::exit(0);
}
int r = uv_read_start((uv_stream_t*)&c->socket, on_alloc_callback, on_read_callback);
if(r == -1) {
printf("ERROR: uv_read_start error: %s\n", uv_err_name(uv_last_error(c->loop)));
::exit(0);
}
const char* http_request_tpl = "" \
"GET / HTTP/1.1\r\n"
"Host: %s\r\n"
"User-Agent: uv_www_client/0.1\r\n"
"Accept: */*\r\n"
"Connection: close\r\n"
"\r\n";
char http_request[1024];
sprintf(http_request, http_request_tpl, c->host);
c->addAppData(http_request);
c->ssl = SSL_new(c->ssl_ctx);
c->read_bio = BIO_new(BIO_s_mem());
c->write_bio = BIO_new(BIO_s_mem());
SSL_set_bio(c->ssl, c->read_bio, c->write_bio);
SSL_set_connect_state(c->ssl);
r = SSL_do_handshake(c->ssl);
on_event(c);
}
void on_resolved_callback(uv_getaddrinfo_t* resolver, int status, struct addrinfo * res) {
Client* c = static_cast<Client*>(resolver->data);
if(status == -1) {
printf("ERROR: getaddrinfo callback error: %s\n", uv_err_name(uv_last_error(c->loop)));
::exit(0);
}
char addr[17] = {'\0'};
uv_ip4_name((struct sockaddr_in*) res->ai_addr, addr, 16);
printf("> %s\n", addr);
uv_tcp_init(c->loop, &c->socket);
uv_tcp_connect(&c->connect_req, &c->socket, *(sockaddr_in*)res->ai_addr, on_connect_callback);
uv_freeaddrinfo(res);
}
// TEST SSL APPLICATION
// --------------------
int main() {
const char* CLIENT_CERT_FILE = "client-cert.pem";
const char* CLIENT_KEY_FILE = "client-key.pem";
// Initialize SSL
SSL_library_init();
SSL_load_error_strings();
//RAND_poll();
BIO* bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_method());
int rc = SSL_CTX_use_certificate_chain_file(ssl_ctx, CLIENT_CERT_FILE);
if(rc != 1) {
printf("Could not load client certificate file.\n");
::exit(1);
}
rc = SSL_CTX_use_PrivateKey_file(ssl_ctx, CLIENT_KEY_FILE, SSL_FILETYPE_PEM);
if(!rc) {
printf("Could not load client key file.\n");
::exit(1);
}
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2);
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL); // no verify, testing libuv + openssl mem bios
SSL_CTX_set_info_callback(ssl_ctx, dummy_ssl_info_callback);
SSL_CTX_set_msg_callback(ssl_ctx, dummy_ssl_msg_callback);
SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_OFF); // testing, you can remove this
// SSL_CTX_set_cipher_list(ssl_ctx, "RC4+RSA:HIGH:+MEDIUM:+LOW"); // testing, you can remove this
// LIB UV
uv_loop_t* loop = uv_loop_new();
// Client context
Client c;
c.loop = loop;
c.connect_req.data = &c;
c.socket.data = &c;
c.ssl = NULL;
c.ssl_ctx = ssl_ctx;
c.http_state = HS_NONE;
sprintf(c.host, "%s", "test.localhost");
sprintf(c.port, "%s", "443");
// Resolve host
struct addrinfo hints;
hints.ai_family = PF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = 0;
uv_getaddrinfo_t resolver;
resolver.data = &c;
int r = uv_getaddrinfo(loop, &resolver, on_resolved_callback, c.host, c.port, &hints);
uv_run(loop);
return 0;
}
@roxlu
Copy link
Author

roxlu commented Oct 31, 2012

    LOOP - UNKWN   - before/connect initialization 
    Message callback with length: 316
    LOOP - 23WCHA  - SSLv2/v3 write client hello A 
    EXIT - 23RSHA  - SSLv2/v3 read server hello A 
    EXIT - 23RSHA  - SSLv2/v3 read server hello A 
    Message callback with length: 53
    LOOP - 3RSH_A  - SSLv3 read server hello A 
    Message callback with length: 1319
countryName               = AU
stateOrProvinceName       = Some-State
organizationName          = Internet Widgits Pty Ltd
commonName                = test.localhost  ssl_verify_callback(), ok: 0, error: 20, depth: 0, name: /C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=test.localhost
countryName               = AU
stateOrProvinceName       = Some-State
organizationName          = Internet Widgits Pty Ltd
commonName                = test.localhost  ssl_verify_callback(), ok: 0, error: 27, depth: 0, name: /C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=test.localhost
countryName               = AU
stateOrProvinceName       = Some-State
organizationName          = Internet Widgits Pty Ltd
commonName                = test.localhost  ssl_verify_callback(), ok: 0, error: 21, depth: 0, name: /C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=test.localhost
    LOOP - 3RSC_A  - SSLv3 read server certificate A 
    Message callback with length: 781
    LOOP - 3RSKEA  - SSLv3 read server key exchange A 
    Message callback with length: 4
    LOOP - 3RSD_A  - SSLv3 read server done A 
    Message callback with length: 134
    LOOP - 3WCKEA  - SSLv3 write client key exchange A 
    Message callback with length: 1
    LOOP - 3WCCSA  - SSLv3 write change cipher spec A 
    Message callback with length: 16
    LOOP - 3WFINA  - SSLv3 write finished A 
    LOOP - 3FLUSH  - SSLv3 flush data 
    EXIT - UNKWN   - SSLv3 read server session ticket A 
    EXIT - UNKWN   - SSLv3 read server session ticket A 
    EXIT - UNKWN   - SSLv3 read server session ticket A 
    Message callback with length: 202
    LOOP - UNKWN   - SSLv3 read server session ticket A 
    Message callback with length: 1
    Message callback with length: 16
    LOOP - 3RFINA  - SSLv3 read finished A 
    HANDSHAKE DONE - SSLOK   - SSL negotiation finished successfully 
    EXIT - SSLOK   - SSL negotiation finished successfully 
> W: 103
> W, in write bio: 10484
> BIO: 10240

@leomax89
Copy link

leomax89 commented Sep 2, 2014

Hi there! Did you manage to get it working?

@excavador
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment