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
    
  • 相关阅读:
    Win64 驱动内核编程-12.回调监控进线程创建和退出
    我的主站 SHARELIST -分享列表 (功能持续完善中 2019-11-24 版本0.3)
    Caddy-基于go的微型serve用来做反向代理和Gateway
    Docker Swarm删除节点
    Docker Swarm集群搭建
    Docker Machine-Windows
    Docker Compose
    Docker网络配置进阶
    Docker网络相关
    Docker数据管理
  • 原文地址:https://www.cnblogs.com/sunminming/p/12032879.html
Copyright © 2011-2022 走看看