libevent笔记6:ssl bufferevent

笑着哭i 提交于 2019-12-13 02:28:42

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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!