Libevent另外提供了基于openssl的bufferevent来支持ssl,通过特殊的ssl bufferevent来对数据进行加密。
ps:本文不对openssl相应的接口做介绍因为不熟
SSL bufferevent相关函数
struct bufferevent bufferevent_openssl_socket_new(struct event_base base, evutil_socket_t fd, struct ssl_st *ssl, enum bufferevent_ssl_state state, int options) 该函数能够基于给定的文件描述符及ssl对象创建一个ssl bufferevent。其中,bufferevent_ssl_state state参数表明了该bufferevent的角色,在作为服务端时一般使用BUFFEREVENT_SSL_ACCEPTING,在作为客户端时一般使用BUFFEREVENT_SSL_CONNECTING。
struct bufferevent bufferevent_openssl_filter_new(struct event_base base, struct bufferevent underlying, struct ssl_st ssl, enum bufferevent_ssl_state state, int options) 该函数能够基于给定的底层bufferevent及ssl对象创建一个过滤器,该过滤器的过滤函数已经由系统通过ssl对象定义好,我们只需另外定义过滤器读写回调函数即可。
ps:在下例的客户端代码中,注释中即为使用过滤器来实现ssl bufferevent。
Demo
- 服务器,这段代码来自Libevent book。服务器的主要工作时回显客户端发来的数据。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <openssl/ssl.h> #include <openssl/err.h> #include <openssl/rand.h> #include <event.h> #include <event2/listener.h> #include <event2/bufferevent_ssl.h> #include "basicev.h"//包含对中断信号的处理事件及回调函数 static void ssl_readcb(struct bufferevent * bev, void * arg) { //将输入缓存区的数据输出 struct evbuffer *in = bufferevent_get_input(bev); printf("Received %zu bytes\n", evbuffer_get_length(in)); printf("----- data ----\n"); printf("%.*s\n", (int)evbuffer_get_length(in), evbuffer_pullup(in, -1)); //将输入缓存区的数据放入输出缓存区发生到客户端 bufferevent_write_buffer(bev, in); } static void ssl_acceptcb(struct evconnlistener *serv, int sock, struct sockaddr *sa, int sa_len, void *arg) { struct event_base *evbase; struct bufferevent *bev; SSL_CTX *server_ctx; SSL *client_ctx; server_ctx = (SSL_CTX *)arg; client_ctx = SSL_new(server_ctx); evbase = evconnlistener_get_base(serv); bev = bufferevent_openssl_socket_new(evbase, sock, client_ctx, BUFFEREVENT_SSL_ACCEPTING, BEV_OPT_CLOSE_ON_FREE); bufferevent_enable(bev, EV_READ); bufferevent_enable(bev, EV_WRITE); bufferevent_setcb(bev, ssl_readcb, NULL, NULL, NULL); char buf[] = "Hello, this is ECHO"; bufferevent_write(bev, buf, sizeof(buf)); } static SSL_CTX *evssl_init(void) { SSL_CTX *server_ctx; /* Initialize the OpenSSL library */ SSL_load_error_strings(); SSL_library_init(); /* We MUST have entropy, or else there's no point to crypto. */ if (!RAND_poll()) return NULL; server_ctx = SSL_CTX_new(SSLv23_server_method()); if (! SSL_CTX_use_certificate_chain_file(server_ctx, "cacert.pem") || ! SSL_CTX_use_PrivateKey_file(server_ctx, "privkey.pem", SSL_FILETYPE_PEM)) { puts("Couldn't read 'pkey' or 'cert' file. To generate a key\n" "and self-signed certificate, run:\n" " openssl genrsa -out pkey 2048\n" " openssl req -new -key pkey -out cert.req\n" " openssl x509 -req -days 365 -in cert.req -signkey pkey -out cert"); return NULL; } SSL_CTX_set_options(server_ctx, SSL_OP_NO_SSLv2); return server_ctx; } int main(int argc, char **argv) { SSL_CTX *ctx; struct evconnlistener *listener; struct event_base *evbase; struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(9999); sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */ //初始化ssl环境 ctx = evssl_init(); if (ctx == NULL) return 1; //初始化event2环境 evbase = event_base_new(); if(evbase == NULL) { printf("%s\n", strerror(errno)); exit(1); } //创建监听器 listener = evconnlistener_new_bind( evbase, ssl_acceptcb, (void *)ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 1024, (struct sockaddr *)&sin, sizeof(sin)); //添加中断信号处理事件 add_signal(evbase); event_base_loop(evbase, 0); evconnlistener_free(listener); SSL_CTX_free(ctx); return 0; }
- 客户端
#include <stdio.h> #include <string.h> #include <errno.h> #include <sys/socket.h> #include <resolv.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <openssl/ssl.h> #include <openssl/err.h> #include <event2/event.h> #include <event2/bufferevent.h> #include <event2/bufferevent_ssl.h> #include "basicev.h" static void read_cb(struct bufferevent *bev, void *arg) { char buf[1024] = {0}; bufferevent_read(bev, buf, 1024); printf("%s\n", buf); } int main(int argc, char **argv) { int sockfd, len; struct sockaddr_in dest; SSL_CTX *ctx; SSL *ssl; //初始化ssl环境 SSL_library_init(); OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); SSL_load_error_strings(); ctx = SSL_CTX_new(SSLv23_client_method()); if (ctx == NULL) { ERR_print_errors_fp(stdout); exit(1); } // 创建一个 socket 用于底层通信 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("Socket: %s\n", strerror(errno)); exit(1); } // 初始化服务器端地址 memset(&dest, 0 ,sizeof(dest)); dest.sin_family = AF_INET; dest.sin_port = htons(9999); if (inet_aton("127.0.0.1", (struct in_addr *) &dest.sin_addr.s_addr) == 0) { exit(errno); } // 连接服务器 if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) { printf("Connect: %s\n ", strerror(errno)); exit(errno); } //初始化event2 struct event_base *base = NULL; struct bufferevent *sslbev = NULL; base = event_base_new(); if(base == NULL) { printf("%s\n", strerror(errno)); exit(1); } //创建一个ssl对象 ssl = SSL_new(ctx); //创建ssl的bufferevent sslbev = bufferevent_openssl_socket_new(base, sockfd, ssl, BUFFEREVENT_SSL_CONNECTING, BEV_OPT_CLOSE_ON_FREE); /* 使用过滤器的ssl bufferevent struct bufferevent *bev = bufferevent_socket_new(base, sockfd, BEV_OPT_CLOSE_ON_FREE); sslbev = bufferevent_openssl_filter_new(base, bev, ssl, BUFFEREVENT_SSL_CONNECTING, BEV_OPT_CLOSE_ON_FREE); */ bufferevent_setcb(sslbev, read_cb, NULL, NULL, NULL); bufferevent_enable(sslbev, EV_READ|EV_WRITE); //添加中断信号处理事件 add_signal(base); //添加标准输入处理事件 //该事件的回调函数会将从标准输入得到的数据写入sslbev add_stdin(base, sslbev); event_base_dispatch(base); bufferevent_free(sslbev); event_base_free(base); SSL_CTX_free(ctx); return 0; }
- 客户端输出
sunminming@sunminming:~/libevent/ssl$ ./client Hello, this is ECHO hello, this is client //这行为手动键入 hello, this is client //这行为服务器回显
- 服务器输出
sunminming@sunminming:~/libevent/ssl$ ./server Received 21 bytes ----- data ---- hello, this is client
来源:https://www.cnblogs.com/sunminming/p/12032879.html