Created
October 31, 2012 18:54
-
-
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 (?)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
Author
roxlu
commented
Oct 31, 2012
Hi there! Did you manage to get it working?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment