Advertisement
Guest User

libevent multithreaded ssl test code

a guest
Mar 27th, 2013
540
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 13.58 KB | None | 0 0
  1. /*
  2.  * Simple test to verify SSL behavior with libevent. This code is
  3.  * intended to demonstrate a specific behavior- it is not meant for
  4.  * any other use. (i.e. it egregiously uses unprotected globals,
  5.  * albeit in a safe manner, for interthread communication)
  6.  *
  7.  * This code operates in the following approximate phases:
  8.  * 1. Initialize libraries.
  9.  * 2. Synchronously connect to server.
  10.  * 3. Once connected, spawn specified number of transmit threads.
  11.  * 4. Forever: Run threads.
  12.  *
  13.  * Each thread loops:
  14.  * 1. Send one line of data to the server.
  15.  * 2. Sleep 0.1s
  16.  * 3. Repeat.
  17.  *
  18.  * NOTE: Some portions of this code may fall under others' copyrights-
  19.  *    Namely the code for initializing SSL multithreaded was taken
  20.  *    directly from the book referenced in the code below.
  21.  *
  22.  * NOTE: This code explicitly runs SSL in a very insecure
  23.  *    manner. Don't copy this code and assume you have any security at
  24.  *    all.
  25.  *
  26.  * NOTE: If your server isn't running, you might get a SIGPIPE, as I
  27.  *    do not disable SIGPIPE in this code.
  28.  *
  29.  * To build: (I have used Centos 6.3 and OSX 10.8.3)
  30.  *
  31.  * gcc -Wall -Werror test.c -o test -lssl -lcrypto -levent -levent_openssl -levent_pthreads
  32.  *
  33.  * If you have openssl/libevent somewhere, you may need to add
  34.  * -I/your_include_dir and -L/your_library_dir paths to the command
  35.  * line above. I do this for OSX, but not for my linux builds.
  36.  *
  37.  * To make a server that can listen, using openssl:
  38.  *
  39.  * 1. Create a self-signed certificate for your server, run the
  40.  *    following command, and fill out the form: (Generates files
  41.  *    test.key and test.crt)
  42.  *
  43.  * openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout test.key -out test.crt
  44.  *
  45.  * 2. Run openssl as a simple server, echoing received traffic to
  46.  *    stdout (listens on port 5000, which is the port this code
  47.  *    connects to):
  48.  *
  49.  * openssl s_server -cert test.crt -key test.key -accept 5000
  50.  *
  51.  * To run:
  52.  *
  53.  * ./test IP_ADDRESS NUMTHREADS
  54.  *
  55.  * This code purposely does NOT do name resolution. To run with one
  56.  * thread, going to localhost:
  57.  *
  58.  * ./test 127.0.0.1 1
  59.  *
  60.  * etc.
  61.  *
  62.  * This code generally writes to stderr.
  63.  */
  64.  
  65. /* The following protect calls to bufferevents with an external
  66.  * mutex. Use/Don't use at your discretion. It should not be required,
  67.  * but is here for debugging/analysis. */
  68. #define TEST_USE_EXTERNAL_LOCKING
  69.  
  70. /* If you don't want to use SSL, you can comment this out. If you
  71.  * don't use ssl, you will need to use a different server to connect
  72.  * to rather than the openssl server. */
  73. #define TEST_USE_SSL
  74.  
  75. /* Behavior is demonstrated with as few as 1 thread. */
  76. #define MAX_THREADS 100
  77.  
  78. #include <stdio.h>
  79. #include <stdlib.h>
  80. #include <string.h>
  81. #include <sys/socket.h>
  82. #include <sys/time.h>
  83. #include <netinet/in.h>
  84. #include <arpa/inet.h>
  85. #include <pthread.h>
  86. #include <unistd.h>
  87.  
  88. #include <openssl/ssl.h>
  89.  
  90. #include <event.h>
  91. #include <event2/thread.h>
  92. #include <event2/bufferevent_ssl.h>
  93.  
  94.  
  95.  
  96.  
  97. /***************************************************
  98.  *
  99.  * OpenSSL thread initialization routines taken from book:
  100.  *
  101.  * Network Security with OpenSSL
  102.  *
  103.  * Viega, John; Messier, Matt; Chandra, Pravir (2009-02-09)
  104.  */
  105. #if defined(WIN32)
  106. #define MUTEX_TYPE HANDLE
  107. #define MUTEX_SETUP(x) (x) = CreateMutex(NULL, FALSE, NULL)
  108. #define MUTEX_CLEANUP(x) CloseHandle(x)
  109. #define MUTEX_LOCK(x) WaitForSingleObject((x), INFINITE)
  110. #define MUTEX_UNLOCK(x) ReleaseMutex(x)
  111. #define THREAD_ID GetCurrentThreadId( )
  112. #else
  113. /* _POSIX_THREADS is normally defined in unistd.h if pthreads are available
  114.    on your platform. */
  115. #define MUTEX_TYPE pthread_mutex_t
  116. #define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL)
  117. #define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x))
  118. #define MUTEX_LOCK(x) pthread_mutex_lock(&(x))
  119. #define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x))
  120. #define THREAD_ID pthread_self( )
  121. #endif
  122. /* This array will store all of the mutexes available to OpenSSL. */
  123. static MUTEX_TYPE *mutex_buf = NULL;
  124. static void locking_function(int mode, int n, const char * file, int line)
  125. {
  126.     if (mode & CRYPTO_LOCK)
  127.         MUTEX_LOCK(mutex_buf[n]);
  128.     else
  129.         MUTEX_UNLOCK(mutex_buf[n]);
  130. }
  131. static unsigned long id_function(void)
  132. {
  133.     return ((unsigned long)THREAD_ID);
  134. }
  135. struct CRYPTO_dynlock_value
  136. {
  137.     MUTEX_TYPE mutex;
  138. };
  139. static struct CRYPTO_dynlock_value * dyn_create_function(const char *file,
  140.                                                          int line)
  141. {
  142.     struct CRYPTO_dynlock_value *value;
  143.     value = (struct CRYPTO_dynlock_value *)malloc(sizeof(
  144.                                                          struct CRYPTO_dynlock_value));
  145.     if (!value)
  146.         return NULL;
  147.     MUTEX_SETUP(value->mutex);
  148.     return value;
  149. }
  150. static void dyn_lock_function(int mode, struct CRYPTO_dynlock_value *l,
  151.                               const char *file, int line)
  152. {
  153.     if (mode & CRYPTO_LOCK)
  154.         MUTEX_LOCK(l->mutex);
  155.     else
  156.         MUTEX_UNLOCK(l->mutex);
  157. }
  158. static void dyn_destroy_function(struct CRYPTO_dynlock_value *l,
  159.                                  const char *file, int line)
  160. {
  161.     MUTEX_CLEANUP(l->mutex);
  162.     free(l);
  163. }
  164. int THREAD_setup(void)
  165. {
  166.     int i;
  167.     mutex_buf = (MUTEX_TYPE *)malloc(CRYPTO_num_locks( ) * sizeof(MUTEX_TYPE));
  168.     if (!mutex_buf)
  169.         return 0;
  170.     for (i = 0; i < CRYPTO_num_locks( ); i++)
  171.         MUTEX_SETUP(mutex_buf[i]);
  172.     CRYPTO_set_id_callback(id_function);
  173.     CRYPTO_set_locking_callback(locking_function);
  174.     /* The following three CRYPTO_... functions are the OpenSSL functions
  175.        for registering the callbacks we implemented above */
  176.     CRYPTO_set_dynlock_create_callback(dyn_create_function);
  177.     CRYPTO_set_dynlock_lock_callback(dyn_lock_function);
  178.     CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function);
  179.     return 1;
  180. }
  181. int THREAD_cleanup(void)
  182. {
  183.     int i;
  184.     if (!mutex_buf)
  185.         return 0;
  186.     CRYPTO_set_id_callback(NULL);
  187.     CRYPTO_set_locking_callback(NULL);
  188.     CRYPTO_set_dynlock_create_callback(NULL);
  189.     CRYPTO_set_dynlock_lock_callback(NULL);
  190.     CRYPTO_set_dynlock_destroy_callback(NULL);
  191.     for (i = 0; i < CRYPTO_num_locks( ); i++)
  192.         MUTEX_CLEANUP(mutex_buf[i]);
  193.     free(mutex_buf);
  194.     mutex_buf = NULL;
  195.     return 1;
  196. }
  197. /*
  198.  * OpenSSL thread initialization code complete.
  199.  *
  200.  ***************************************************/
  201.  
  202.  
  203. #ifdef TEST_USE_EXTERNAL_LOCKING
  204. static MUTEX_TYPE global_lock;
  205. #endif
  206.  
  207.  
  208.  
  209. /* Yes, lovely globals */
  210. struct event_base *ev_base;
  211. struct bufferevent *ev_buf;
  212. struct event *ev_forever;
  213. int num_threads = 0;
  214. int thread_numbers[MAX_THREADS];
  215. pthread_t thread_ids[MAX_THREADS];
  216.  
  217.  
  218. /*
  219.  * Simple thread. Just writes one line of data to output, once every 0.1 seconds.
  220.  *
  221.  * arg points at an int containing our (user-defined) thread number.
  222.  */
  223. static void *run_thread(void *arg)
  224. {
  225.     int id = *((int *) arg);
  226.     char str[100];
  227.     int len;
  228.     int count = 0;
  229.    
  230.     fprintf(stderr, "Thread %d starting, ID = %ld, %016lx \n", id, id_function(), id_function());
  231.     while(1) {
  232.         len = snprintf(str, sizeof(str), "Thread %d sending data, iteration %d\n", id, count);
  233.         if (len >= sizeof(str)) {
  234.             fprintf(stderr, "Erk, Line got long.\n");
  235.             exit(1);
  236.         }
  237.         fprintf(stderr, "%s", str);
  238.  
  239. #ifdef TEST_USE_EXTERNAL_LOCKING
  240.         MUTEX_LOCK(global_lock);
  241. #endif
  242.         if (bufferevent_write(ev_buf, str, len)) {
  243. #ifdef TEST_USE_EXTERNAL_LOCKING
  244.             MUTEX_UNLOCK(global_lock);
  245. #endif
  246.             fprintf(stderr, "Error writing, thread %d, at iteration %d\n", id, count);
  247.             exit(1);
  248.         }
  249. #ifdef TEST_USE_EXTERNAL_LOCKING
  250.         MUTEX_UNLOCK(global_lock);
  251. #endif
  252.         usleep(100000);
  253.         count++;
  254.     }
  255. }
  256.  
  257. int test_ssl_init(void)
  258. {
  259.     THREAD_setup();
  260.     SSL_load_error_strings();
  261.     SSL_library_init();
  262.  
  263.     printf("SSL version: %s\n", SSLeay_version(SSLEAY_VERSION));
  264.     return 0;
  265. }
  266.  
  267. int test_libevent_init(void)
  268. {
  269.     printf("Libevent version: %s\n", event_get_version());
  270.  
  271.     if (evthread_use_pthreads() != 0) {
  272.         fprintf(stderr, "Error: libevent not using threads\n");
  273.         exit(1);
  274.     }
  275.  
  276.     return 0;
  277. }
  278.  
  279. /*
  280.  * A simple timer event, to show that we have not deadlocked.
  281.  */
  282. static void timer_cb(evutil_socket_t sock, short flags, void *cookie)
  283. {
  284.     fprintf(stderr, "Time passes\n");
  285. }
  286.  
  287. /*
  288.  * For completeness: A read callback.
  289.  */
  290. static void read_cb(struct bufferevent *ev_buf, void *cookie)
  291. {
  292.     fprintf(stderr, "Received read callback\n");
  293. }
  294.  
  295. /*
  296.  * For completeness: A write callback.
  297.  */
  298. static void write_cb(struct bufferevent *ev_buf, void *cookie)
  299. {
  300.     fprintf(stderr, "Received write callback\n");
  301. }
  302.  
  303. /*
  304.  * All the events we see. On connect, we spawn our threads.
  305.  */
  306. static void event_cb(struct bufferevent *bev, short style, void *cookie)
  307. {
  308.     fprintf(stderr, "Received event :");
  309.     if (style & BEV_EVENT_READING) fprintf (stderr, " BEV_EVENT_READING");
  310.     if (style & BEV_EVENT_WRITING) fprintf (stderr, " BEV_EVENT_WRITING");
  311.     if (style & BEV_EVENT_EOF) fprintf (stderr, " BEV_EVENT_EOF");
  312.     if (style & BEV_EVENT_ERROR) fprintf (stderr, " BEV_EVENT_ERROR");
  313.     if (style & BEV_EVENT_TIMEOUT) fprintf (stderr, " BEV_EVENT_TIMEOUT");
  314.     if (style & BEV_EVENT_CONNECTED) fprintf (stderr, " BEV_EVENT_CONNECTED");
  315.     fprintf(stderr, "\n");
  316.    
  317.     if (style & BEV_EVENT_CONNECTED) {
  318.         /* Start threads. */
  319.         int i;
  320.         for (i = 0; i < num_threads; i++) {
  321.             fprintf(stderr, "Creating thread %d\n", i);
  322.             thread_numbers[i] = i;
  323.             if (pthread_create(&(thread_ids[i]), NULL, run_thread, &(thread_numbers[i]))) {
  324.                 fprintf(stderr, "Err: Thread creation failed for thread %d\n", i);
  325.                 exit(1);
  326.             }
  327.         }
  328.     }
  329.    
  330.     if (style & BEV_EVENT_ERROR) {
  331.         fprintf(stderr, "Is your server running?\n");
  332.     }
  333. }
  334.  
  335. int main (int argc, char *argv[])
  336. {
  337.     SSL_CTX *ssl_ctx;
  338.     SSL *ssl;
  339.     struct sockaddr_in sin;
  340.     struct timeval tv;
  341.     int res;
  342.            
  343. #ifdef TEST_USE_EXTERNAL_LOCKING
  344.     MUTEX_SETUP(global_lock);
  345. #endif
  346.  
  347.     if (argc != 3) {
  348.         fprintf(stderr, "Use %s <IP-ADDRESS> <NUM-THREADS>\n", argv[0]);
  349.         exit(1);
  350.     }
  351.  
  352.     num_threads = atoi(argv[2]);
  353.     if ((num_threads > MAX_THREADS) || (num_threads < 0)) {
  354.         fprintf(stderr, "Invalid number of threads. 0 to %d, please.\n", MAX_THREADS);
  355.         exit(1);
  356.     }
  357.  
  358.     res = test_ssl_init();
  359.     if (res) {
  360.         fprintf(stderr, "Err: test_ssl_init returned %d\n", res);
  361.         exit(1);
  362.     }
  363.  
  364.     res = test_libevent_init();
  365.     if (res) {
  366.         fprintf(stderr, "Err: test_libevent_init returned %d\n", res);
  367.         exit(1);
  368.     }
  369.  
  370.     ssl_ctx = SSL_CTX_new(SSLv3_client_method());
  371.     if (!ssl_ctx) {
  372.         fprintf(stderr, "Err: Could not create SSL context\n");
  373.         exit(1);
  374.     }
  375.  
  376.     /* Don't verify certificates. THIS IS EXTRAORDINARILY BAD for
  377.      * security. It is done here ONLY TO TEST. */
  378.     SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
  379.  
  380.     ssl = SSL_new(ssl_ctx);
  381.     if (!ssl) {
  382.         fprintf(stderr, "Err: Could not create SSL\n");
  383.         exit(1);
  384.     }
  385.    
  386.     ev_base = event_base_new();
  387.     if (!ev_base) {
  388.         fprintf(stderr, "Err: event_base_new failed\n");
  389.         exit(1);
  390.     }
  391.  
  392.     /* Create a timer that runs forever. Handy to see timing, handy to
  393.      * have something run forever in dispatch. */
  394.     ev_forever = event_new(ev_base, -1, EV_PERSIST, timer_cb, NULL);
  395.     if (!ev_forever) {
  396.         fprintf(stderr, "Err: Could not create forever event.\n");
  397.         exit(1);
  398.     }
  399.     tv.tv_sec = 1;
  400.     tv.tv_usec = 0;
  401.     if (event_add(ev_forever, &tv)) {
  402.         fprintf(stderr, "Err: Could not add forever event\n");
  403.         exit(1);
  404.     }
  405.  
  406. #ifdef TEST_USE_SSL
  407.     ev_buf = bufferevent_openssl_socket_new(ev_base,
  408.                                             -1,
  409.                                             ssl,
  410.                                             BUFFEREVENT_SSL_CONNECTING,
  411.                                             BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE);
  412. #else
  413.     ev_buf = bufferevent_socket_new(ev_base,
  414.                                     -1,
  415.                                     BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE);
  416. #endif
  417.     if (!ev_buf) {
  418.         fprintf(stderr, "Err: Could not create ev_buf\n");
  419.         exit(1);
  420.     }
  421.  
  422.     bufferevent_setcb(ev_buf, read_cb, write_cb, event_cb, NULL);
  423.  
  424.     /* These should not be needed, but I threw them in here for good
  425.      * luck. */
  426.     evbuffer_enable_locking(bufferevent_get_input(ev_buf), NULL);
  427.     evbuffer_enable_locking(bufferevent_get_output(ev_buf), NULL);
  428.  
  429.     memset(&sin, 0, sizeof(sin));
  430. #ifndef __linux__
  431.     sin.sin_len = sizeof(sin);
  432. #endif
  433.     sin.sin_family = AF_INET;
  434.     sin.sin_port = htons(5000);
  435.     sin.sin_addr.s_addr = inet_addr(argv[1]);
  436.  
  437.     res = bufferevent_socket_connect(ev_buf,
  438.                                      (struct sockaddr *) &sin,
  439.                                      sizeof(sin));
  440.     if (res != 0) {
  441.         fprintf(stderr,
  442.                 "Err: bufferevent_socket_connect_hostname returned %d\n",
  443.                 res);
  444.         exit(1);
  445.     }
  446.  
  447.     bufferevent_enable(ev_buf, EV_READ|EV_WRITE);
  448.  
  449.     /* Note: No locking really required before this point, as no other
  450.      * threads are running yet. Threads are started once connect event
  451.      * returns. */
  452.     fprintf(stderr, "Starting dispatch loop\n");
  453.    
  454.     event_base_dispatch(ev_base);
  455.    
  456.     fprintf(stderr, "Leaving dispatch loop\n");
  457.  
  458.     exit(0);
  459. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement