zoukankan      html  css  js  c++  java
  • libevent笔记6:ssl bufferevent

    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
    ", evbuffer_get_length(in));
       printf("----- data ----
    ");
       printf("%.*s
    ", (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
    "
               "and self-signed certificate, run:
    "
               "  openssl genrsa -out pkey 2048
    "
               "  openssl req -new -key pkey -out cert.req
    "
               "  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
    ", 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
    ", 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
    ", 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
     ", strerror(errno));
          exit(errno);
       }
       
       //初始化event2
       struct event_base *base = NULL;
       struct bufferevent *sslbev = NULL;
    
       base = event_base_new();
       if(base == NULL)
       {
          printf("%s
    ", 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
    
  • 相关阅读:
    165. Compare Version Numbers
    164. Maximum Gap
    3、桶排序
    162. Find Peak Element
    160. Intersection of Two Linked Lists
    155. Min Stack
    154. Find Minimum in Rotated Sorted Array II
    153. Find Minimum in Rotated Sorted Array
    Linux/Unix系统编程手册 第二章:基本概念
    Linux/Unix系统编程手册 第一章:历史和标准
  • 原文地址:https://www.cnblogs.com/sunminming/p/12032879.html
Copyright © 2011-2022 走看看