/* * LibNoPoll: A websocket library * Copyright (C) 2013 Advanced Software Production Line, S.L. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA * * You may find a copy of the license under this software is released * at COPYING file. This is LGPL software: you are welcome to develop * proprietary applications using this library without any royalty or * fee but returning back any change, improvement or addition in the * form of source code, project image, documentation patches, etc. * * For commercial support on build Websocket enabled solutions * contact us: * * Postal address: * Advanced Software Production Line, S.L. * Edificio Alius A, Oficina 102, * C/ Antonio Suarez Nº 10, * Alcalá de Henares 28802 Madrid * Spain * * Email address: * info@aspl.es - http://www.aspl.es/nopoll */ #include #include #include "freertos/FreeRTOS.h" /** * \defgroup nopoll_support noPoll Support: core support functions used by the library */ /** * \addtogroup nopoll_support * @{ */ /** * @brief Allows to check if provided strings are equal. * * @param string1 The first string to check. The string must be ended by 0. * @param string2 The second string to check. The string must be ended by 0. * * @return nopoll_true if both strings are equal, otherwise * nopoll_false is returned. */ nopoll_bool nopoll_cmp (const char * string1, const char * string2) { int iterator; if (string1 == NULL && string2 == NULL) return nopoll_true; if (string1 == NULL || string2 == NULL) return nopoll_false; /* next position */ iterator = 0; while (string1[iterator] && string1[iterator]) { if (string1[iterator] != string2[iterator]) return nopoll_false; iterator++; } /* end while */ /* last check, ensure both ends with 0 */ return string1[iterator] == string2[iterator]; } /** * @brief Allows to check if provided strings are equal for the first * bytes. * * @param string1 The first string to check. The string must be ended by 0. * @param string2 The second string to check. The string must be ended by 0. * @param bytes Number of bytes to check. The value must be > 0. * * @return nopoll_true if both strings are equal, otherwise * nopoll_false is returned. */ nopoll_bool nopoll_ncmp (const char * string1, const char * string2, int bytes) { int iterator; if (bytes <= 0) return nopoll_false; if (string1 == NULL && string2 == NULL) return nopoll_true; if (string1 == NULL || string2 == NULL) return nopoll_false; /* next position */ iterator = 0; while (string1[iterator] && string2[iterator] && iterator < bytes) { if (string1[iterator] != string2[iterator]) return nopoll_false; iterator++; } /* end while */ /* last check, ensure both ends with 0 */ return iterator == bytes; } /** * @brief Allows to produce an newly allocated string produced by the * chunk received plus arguments, using the printf-like format. * * @param chunk The chunk to copy. * * @return A newly allocated chunk. */ char * nopoll_strdup_printf (const char * chunk, ...) { char * result = NULL; va_list args; if (chunk == NULL) return NULL; /* open std args */ va_start (args, chunk); /* get the string */ result = nopoll_strdup_printfv (chunk, args); /* close std args */ va_end (args); return result; } /** * @internal Allows to calculate the amount of memory required to * store the string that will representing the construction provided * by the printf-like format received and its arguments. * * @param format The printf-like format to be printed. * * @param args The set of arguments that the printf applies to. * * NOTE: not all printf specification is supported. Generally, the * following is supported: %s, %d, %f, %g, %ld, %lg and all * combinations that provides precision, number of items inside the * integer part, etc: %6.2f, %+2d, etc. An especial case not supported * is %lld, %llu and %llg. * * @return Return the number of bytes that must be allocated to hold * the string (including the string terminator \0). If the format is * not correct or it is not properly formated according to the value * found at the argument set, the function will return -1. */ int nopoll_vprintf_len (const char * format, va_list args) { /** IMPLEMENTATION NOTE: in the case this code is update, * update exarg_vprintf_len **/ #if defined (NOPOLL_OS_WIN32) && ! defined (__GNUC__) # if HAVE_VSCPRINTF if (format == NULL) return 0; return _vscprintf (format, args) + 1; # else char buffer[8192]; if (format == NULL) return 0; return _vsnprintf (buffer, 8191, format, args) + 1; # endif #else /* gnu gcc case */ if (format == NULL) return 0; return vsnprintf (NULL, 0, format, args) + 1; #endif } /** * @brief DEPRECATED: Allows to produce an string representing the * message hold by chunk with the parameters provided. * * @param chunk The message chunk to print. * @param args The arguments for the chunk. * * @return A newly allocated string. * * IMPLEMENTATION NOTE: This function may have a fundamental bug due * to the design of va_list arguments under amd64 platforms. In short, * a function receiving a va_list argument can't use it twice. In you * are running amd64, check your nopoll_config.h did find * NOPOLL_HAVE_VASPRINTF. */ char * nopoll_strdup_printfv (const char * chunk, va_list args) { /** IMPLEMENTATION NOTE: place update exarg_strdup_printfv * code in the case this code is updated **/ #if defined(SHOW_DEBUG_LOG) && ! defined(NOPOLL_HAVE_VASPRINTF) noPollCtx * ctx = NULL; #endif #if !defined(NOPOLL_HAVE_VASPRINTF) int size; #endif char * result = NULL; if (chunk == NULL) return NULL; #ifdef NOPOLL_HAVE_VASPRINTF /* do the operation using the GNU extension */ if (vasprintf (&result, chunk, args) == -1) return NULL; #else /* get the amount of memory to be allocated */ size = nopoll_vprintf_len (chunk, args); /* check result */ if (size == -1) { nopoll_log (ctx, NOPOLL_LEVEL_CRITICAL, "unable to calculate the amount of memory for the strdup_printf operation"); return NULL; } /* end if */ /* allocate memory */ result = nopoll_new (char, size + 2); /* copy current size */ # if defined(NOPOLL_OS_WIN32) && ! defined (__GNUC__) size = _vsnprintf_s (result, size + 1, size, chunk, args); # else size = vsnprintf (result, size + 1, chunk, args); # endif #endif /* return the result */ return result; } /** * @internal Function used by \ref nopoll_trim. */ nopoll_bool nopoll_is_white_space (char * chunk) { /* do not complain about receive a null refernce chunk */ if (chunk == NULL) return nopoll_false; if (chunk[0] == ' ') return nopoll_true; if (chunk[0] == '\n') return nopoll_true; if (chunk[0] == '\t') return nopoll_true; if (chunk[0] == '\r') return nopoll_true; /* no white space was found */ return nopoll_false; } /** * @brief Removes white spaces and new lines characters from the * string providing the count of bytes trimmed from the string. * * @param chunk The chunk to trim. * * @param trimmed An optional reference that returns the count of bytes * trimmed by the operation. */ void nopoll_trim (char * chunk, int * trimmed) { int iterator; int iterator2; int end; int total; /* perform some environment check */ if (chunk == NULL) return; /* check empty string received */ if (strlen (chunk) == 0) { if (trimmed) *trimmed = 0; return; } /* check the amount of white spaces to remove from the * begin */ iterator = 0; while (chunk[iterator] != 0) { /* check that the iterator is not pointing to a white * space */ if (! nopoll_is_white_space (chunk + iterator)) break; /* update the iterator */ iterator++; } /* check for the really basic case where an empty string is found */ if (iterator == (int) strlen (chunk)) { /* an empty string, trim it all */ chunk [0] = 0; if (trimmed) *trimmed = iterator; return; } /* end if */ /* now get the position for the last valid character in the * chunk */ total = strlen (chunk) -1; end = total; while (chunk[end] != 0) { /* stop if a white space is found */ if (! nopoll_is_white_space (chunk + end)) { break; } /* update the iterator to eat the next white space */ end--; } /* the number of items trimmed */ total -= end; total += iterator; /* copy the exact amount of non white spaces items */ iterator2 = 0; while (iterator2 < (end - iterator + 1)) { /* copy the content */ chunk [iterator2] = chunk [iterator + iterator2]; /* update the iterator */ iterator2++; } chunk [ end - iterator + 1] = 0; if (trimmed != NULL) *trimmed = total; /* return the result reference */ return; } /** * @brief Portable subsecond sleep. Suspends the calling thread during * the provided amount of time. * * @param microseconds The amount of time to wait. */ void nopoll_sleep (long microseconds) { #if defined(NOPOLL_OS_UNIX) // usleep (microseconds); vTaskDelay(microseconds / portTICK_RATE_MS); return; #elif defined(NOPOLL_OS_WIN32) Sleep (microseconds / 1000); return; #endif } noPollMutexCreate __nopoll_mutex_create = NULL; noPollMutexDestroy __nopoll_mutex_destroy = NULL; noPollMutexLock __nopoll_mutex_lock = NULL; noPollMutexUnlock __nopoll_mutex_unlock = NULL; /** * @brief Creates a mutex with the defined create mutex handler. * * See \ref nopoll_thread_handlers for more information. * * @return A mutex reference or NULL if it fails. */ noPollPtr nopoll_mutex_create (void) { if (! __nopoll_mutex_create) return NULL; /* call defined handler */ return __nopoll_mutex_create (); } /** * @brief Implements a mutex lock operation on the provided * reference. The function just skip when no mutex reference is * received o no lock handler was defined. * * See \ref nopoll_thread_handlers for more information. * * @param mutex The mutex where do the lock operation. * * The function will just return if the reference isn't defined or the * lock handler wasn't installed. */ void nopoll_mutex_lock (noPollPtr mutex) { if (! __nopoll_mutex_lock) return; /* call defined handler */ __nopoll_mutex_lock (mutex); return; } /** * @brief Implements a mutex unlock operation on the provided * reference. The function just skip when no mutex reference is * received o no unlock handler was defined. * * See \ref nopoll_thread_handlers for more information. * * @param mutex The mutex where do the unlock operation. * */ void nopoll_mutex_unlock (noPollPtr mutex) { if (! __nopoll_mutex_unlock) return; /* call defined handler */ __nopoll_mutex_unlock (mutex); return; } /** * @brief Implements a mutex destroy operation on the provided * reference. The function just skip when no mutex reference is * received o no destroy handler was defined. * * See \ref nopoll_thread_handlers for more information. * * @param mutex The mutex to destroy operation. * */ void nopoll_mutex_destroy (noPollPtr mutex) { if (! __nopoll_mutex_destroy) return; /* call defined handler */ __nopoll_mutex_destroy (mutex); return; } /** * @brief Global optional mutex handlers used by noPoll library to * create, destroy, lock and unlock mutex. * * If you set this handlers, the library will use these functions to * secure sensitive code paths that mustn't be protected while working * with threads. * * If you don't provide these, the library will work as usual without * doing any locking. * * @param mutex_create The handler used to create mutexes. * * @param mutex_destroy The handler used to destroy mutexes. * * @param mutex_lock The handler used to lock a particular mutex. * * @param mutex_unlock The handler used to unlock a particular mutex. * * The function must receive all handlers defined, otherwise no * configuration will be done. */ void nopoll_thread_handlers (noPollMutexCreate mutex_create, noPollMutexDestroy mutex_destroy, noPollMutexLock mutex_lock, noPollMutexUnlock mutex_unlock) { /* check handlers before configuring anything */ if (! mutex_create || ! mutex_destroy || ! mutex_lock || ! mutex_unlock) return; /* configured received handlers */ __nopoll_mutex_create = mutex_create; __nopoll_mutex_destroy = mutex_destroy; __nopoll_mutex_lock = mutex_lock; __nopoll_mutex_unlock = mutex_unlock; return; } /** * @brief Allows to encode the provided content, leaving the output on * the buffer allocated by the caller. * * @param content The content to be encoded. * * @param length Content byte-length to encode. * * @param output Reference to the already allocated buffer where to * place the output. * * @param output_size The buffer size. * * @return nopoll_true if the conversion was properly done, otherwise * nopoll_false is returned. The function also returns nopoll_false in * the case some parameter is not defined. */ nopoll_bool nopoll_base64_encode (const char * content, int length, char * output, int * output_size) { base64_encode(output, *output_size, (int*)output_size, (char *)content,length); return nopoll_true; #if 0 BIO * b64; BIO * bmem; BUF_MEM * bptr; if (content == NULL || output == NULL || length <= 0 || output_size == NULL) return nopoll_false; /* create bio */ b64 = BIO_new (BIO_f_base64()); bmem = BIO_new (BIO_s_mem()); /* push */ b64 = BIO_push(b64, bmem); if (BIO_write (b64, content, length) != length) { BIO_free_all (b64); return nopoll_false; } if (BIO_flush (b64) != 1) { BIO_free_all (b64); return nopoll_false; } /* now get content */ BIO_get_mem_ptr (b64, &bptr); /* check output size */ if ((*output_size) < bptr->length) { BIO_free_all (b64); *output_size = bptr->length; return nopoll_false; } memcpy(output, bptr->data, bptr->length - 1); output[bptr->length-1] = 0; BIO_free_all (b64); return nopoll_true; #endif } /** * @brief Decodes the provided base64 content into the user provided * buffer. * * @param content The content to be decoded. * * @param length Content byte-length to encode. * * @param output Reference to the already allocated buffer where to * place the output. * * @param output_size The buffer size. * * @return nopoll_true if the conversion was properly done, otherwise * nopoll_false is returned. The function also returns nopoll_false in * the case some parameter is not defined. */ nopoll_bool nopoll_base64_decode (const char * content, int length, char * output, int * output_size) { int ret; ret = base64_decode(content,length,output,output_size); return ret; #if 0 BIO * b64; BIO * bmem; if (content == NULL || output == NULL || length <= 0 || output_size == NULL) return nopoll_false; /* create bio */ bmem = BIO_new_mem_buf ((void *) content, length); b64 = BIO_new (BIO_f_base64()); BIO_set_flags (b64, BIO_FLAGS_BASE64_NO_NL); /* push */ bmem = BIO_push(b64, bmem); *output_size = BIO_read (bmem, output, *output_size); output[*output_size] = 0; BIO_free_all (bmem); return nopoll_true; #endif } /** * @brief Performs a timeval substract leaving the result in * (result). Subtract the `struct timeval' values a and b, storing the * result in result. * * @param a First parameter to substract * * @param b Second parameter to substract * * @param result Result variable. Do no used a or b to place the * result. * * @return 1 if the difference is negative, otherwise 0 (operations * implemented is a - b). */ int nopoll_timeval_substract (struct timeval * a, struct timeval * b, struct timeval * result) { /* Perform the carry for the later subtraction by updating * y. */ if (a->tv_usec < b->tv_usec) { int nsec = (b->tv_usec - a->tv_usec) / 1000000 + 1; b->tv_usec -= 1000000 * nsec; b->tv_sec += nsec; } if (a->tv_usec - b->tv_usec > 1000000) { int nsec = (a->tv_usec - b->tv_usec) / 1000000; b->tv_usec += 1000000 * nsec; b->tv_sec -= nsec; } /* get the result */ result->tv_sec = a->tv_sec - b->tv_sec; result->tv_usec = a->tv_usec - b->tv_usec; /* return 1 if result is negative. */ return a->tv_sec < b->tv_sec; } /** * @brief Safe strdup () wrapper. * * @param buffer The string to copy * * @return A reference to the string copied or NULL if it fails. */ char * nopoll_strdup (const char * buffer) { if (buffer == NULL) return NULL; return strdup (buffer); } /* internal reference to track if we have to randomly init seed */ nopoll_bool __nopoll_nonce_init = nopoll_false; /** * @brief Fills the buffer provided with a random nonce of the * requested size. The function try to read random bytes from the * local PRNG to complete the bytes requested on the buffer.. * * @param buffer The buffer where the output is left * * @param nonce_size The size of the requested nonce to written into the caller buffer. * * @return nopoll_true if the nonce was created otherwise nopoll_false * is returned. */ nopoll_bool nopoll_nonce (char * buffer, int nonce_size) { long int random_value; int iterator; struct timeval tv; if (buffer == NULL || nonce_size <= 0) return nopoll_false; if (! __nopoll_nonce_init) { #if defined(NOPOLL_OS_WIN32) nopoll_win32_gettimeofday (&tv, NULL); #else gettimeofday (&tv, NULL); #endif // srand (time(0) * tv.tv_usec); __nopoll_nonce_init = nopoll_true; } /* end if */ /* now get the value from random */ iterator = 0; while (iterator < nonce_size) { /* gen random value */ #if defined(NOPOLL_OS_WIN32) random_value = rand (); #else random_value = os_random(); #endif /* copy into the buffer */ memcpy (buffer + iterator, &random_value, sizeof (random_value)); iterator += sizeof (random_value); } /* end while */ return nopoll_true; } /** * @internal Allows to extract a particular bit from a byte given the * position. * * +------------------------+ * | 7 6 5 4 3 2 1 0 | position * +------------------------+ */ int nopoll_get_bit (char byte, int position) { return ( ( byte & (1 << position) ) >> position); } /** * @internal Allows to set a particular bit on the first position of * the buffer provided. * * +------------------------+ * | 7 6 5 4 3 2 1 0 | position * +------------------------+ */ void nopoll_set_bit (char * buffer, int position) { buffer[0] |= (1 << position); return; } void nopoll_show_byte (noPollCtx * ctx, char byte, const char * label) { nopoll_log (ctx, NOPOLL_LEVEL_DEBUG, " byte (%s) = %d %d %d %d %d %d %d %d", label, nopoll_get_bit (byte, 7), nopoll_get_bit (byte, 6), nopoll_get_bit (byte, 5), nopoll_get_bit (byte, 4), nopoll_get_bit (byte, 3), nopoll_get_bit (byte, 2), nopoll_get_bit (byte, 1), nopoll_get_bit (byte, 0)); return; } char * nopoll_int2bin (int a, char *buffer, int buf_size) { int i; buffer += (buf_size - 1); for (i = 31; i >= 0; i--) { *buffer-- = (a & 1) + '0'; a >>= 1; } return buffer; } #define BUF_SIZE 33 void nopoll_int2bin_print (noPollCtx * ctx, int value) { char buffer[BUF_SIZE]; buffer[BUF_SIZE - 1] = '\0'; nopoll_int2bin (value, buffer, BUF_SIZE - 1); nopoll_log (ctx, NOPOLL_LEVEL_DEBUG, "%d = %s", value, buffer); return; } /** * @internal Allows to get the 16 bit integer located at the buffer * pointer. * * @param buffer The buffer pointer to extract the 16bit integer from. * * @return The 16 bit integer value found at the buffer pointer. */ int nopoll_get_16bit (const char * buffer) { int high_part = buffer[0] << 8; int low_part = buffer[1] & 0x000000ff; return (high_part | low_part) & 0x000000ffff; } /** * @internal Allows to get the 8bit integer located at the buffer * pointer. * * @param buffer The buffer pointer to extract the 8bit integer from. * * @erturn The 8 bit integer value found at the buffer pointer. */ int nopoll_get_8bit (const char * buffer) { return buffer[0] & 0x00000000ff; } /** * @internal Allows to set the 16 bit integer value into the 2 first * bytes of the provided buffer. * * @param value The value to be configured in the buffer. * * @param buffer The buffer where the content will be placed. */ void nopoll_set_16bit (int value, char * buffer) { buffer[0] = (value & 0x0000ff00) >> 8; buffer[1] = value & 0x000000ff; return; } /** * @internal Allows to set the 32 bit integer value into the 4 first * bytes of the provided buffer. * * @param value The value to be configured in the buffer. * * @param buffer The buffer where the content will be placed. */ void nopoll_set_32bit (int value, char * buffer) { buffer[0] = (value & 0x00ff000000) >> 24; buffer[1] = (value & 0x0000ff0000) >> 16; buffer[2] = (value & 0x000000ff00) >> 8; buffer[3] = value & 0x00000000ff; return; } /** * @brief Allows to get a 32bits integer value from the buffer. * * @param buffer The buffer where the integer will be retreived from. * * @return The integer value reported by the buffer. */ int nopoll_get_32bit (const char * buffer) { int part1 = (int)(buffer[0] & 0x0ff) << 24; int part2 = (int)(buffer[1] & 0x0ff) << 16; int part3 = (int)(buffer[2] & 0x0ff) << 8; int part4 = (int)(buffer[3] & 0x0ff); return part1 | part2 | part3 | part4; } extern nopoll_bool __nopoll_tls_was_init; /** * @brief Optional function that can be called at the very end of the * noPoll usage to ensure all memory allocated by the library is * released so debugging operations are easier. * * It is not required to call this function to get proper operation by * the library. It is provided, especially due to TLS, to release all * internal memory that may be allocated. */ void nopoll_cleanup_library (void) { if (__nopoll_tls_was_init) { EVP_cleanup (); CRYPTO_cleanup_all_ex_data (); ERR_free_strings (); /* notify the library isn't initialized */ __nopoll_tls_was_init = nopoll_false; } /* end if */ return; } /* end if */ /* @} */ /** * \mainpage * * \section intro noPoll: a toolkit to add WebSocket support to your project * * noPoll is a clean implemetnation of the RFC 6455 : The Websocket protocol definition, written in ANSI C. * * Some of its features are: * * - Context based API design making the library stateless. Support to run several execution contexts in the same process. * - Support for stream based API and message based API (handler notified) * - Robust and well tested implementation checked by a strong regression test to ensure the library keeps on working as new features are added. * - Flexible design that allows its integration into a external loop or to use its own waiting loop * - Support for port share which allows running native protocol and WebSocket protocol on the same port. * * noPoll has been developed by Advanced Software Production Line, * S.L. (http://www.aspl.es). It is \ref license "licensed under the LGPL 2.1" * which allows open source and commercial usage. * * noPoll manual is available in the following link: * * - \ref nopoll_core_library_manual * - \ref license * * Check out noPoll API documentation in the following links: * * 1) Common API definition: * * - \ref nopoll_support * - \ref nopoll_decl_module * - \ref nopoll_ctx * - \ref nopoll_handlers * - \ref nopoll_log * - \ref nopoll_loop * * 2) Function for creating connections and listeners * * - \ref nopoll_conn * - \ref nopoll_listener * - \ref nopoll_conn_opts * * 3) Functions to handle messages (message based API): * * - \ref nopoll_msg * * \section contact_aspl Contact Us * * You can reach us at the noPoll mailing list: at noPoll users * for any question you may find. * * If you are interested on getting commercial support, you can also * contact us at: info@aspl.es. */ /** * \page nopoll_core_library_manual noPoll core library manual * * \section nopoll_core_library_manual_index Index * * Section 1: Basic concepts to starting writing with or integrating noPoll: * * - \ref installing_nopoll * - \ref using_nopoll * - \ref thread_safety * - \ref creating_a_nopoll_ctx * - \ref creating_basic_web_socket_server * - \ref creating_basic_web_socket_client * - \ref nopoll_manual_reading_content_from_connection * - \ref nopoll_manual_handling_fragments_with_websocket * * Section 2: Advanced concepts to consider: * * - \ref nopoll_manual_retrying_write_operations * - \ref nopoll_implementing_port_sharing * * Section 3: using noPoll TLS API: * * - \ref nopoll_implementing_mutual_auth * - \ref nopoll_implementing_tls_extended_validation_post_check * - \ref nopoll_implementing_tls_context_creator * * Section 4: Android platfom notes: * * - \ref nopoll_android_usage "4.1 Android noPoll's usage" * * \section installing_nopoll 1.1 How to install noPoll * * Currently, noPoll has only one dependency, which is OpenSSL * (libssl) for all those crypto operations required by the protocol * itself. * * After having that library installed in your system (check your OS * documentation), download lastest tar.gz noPoll library from: http://code.google.com/p/no-poll * * Then, to compile the library follow the standard autoconf voodoo: * * \code * >> tar xzvf nopoll-{version}.tar.gz * >> cd nopoll-{version} * >> ./configure * >> make * \endcode * * At this point, if everything went ok, you can check the library by running in one terminal: * * \code * >> cd test/ * >> ./nopoll-regression-listener * \endcode * * And then in another terminal the client regression test: * * \code * >> cd test/ * >> ./nopoll-regression-client * \endcode * * If everything looks fine, you can install nopoll into your system with the standard: * \code * >> make install * \endcode * * \section using_nopoll 1.2. How to use noPoll library into your application * * After a successful noPoll installation, you should be able to * include noPoll API functions by including the following header: * * \code * #include * \endcode * * Then you can use the following to get nopoll compilation flags: * * \code * >> pkg-config nopoll --cflags * >> pkg-config nopoll --libs * \endcode * * Or if your project uses autoconf for the building process, just include the following into your configure.ac file: * * \code * dnl check for websocket support (through noPoll) * AC_ARG_ENABLE(websocket-support, [ --disable-websocket-support Makes the built with WebSocket extension library], * enable_websocket_support="$enableval", * enable_websocket_support=yes) * if test "$enable_websocket_support" != "no" ; then * PKG_CHECK_MODULES(NOPOLL, nopoll, [enable_websocket_support=yes], [enable_websocket_support=no]) * AC_SUBST(NOPOLL_CFLAGS) * AC_SUBST(NOPOLL_LIBS) * fi * AM_CONDITIONAL(ENABLE_WEBSOCKET_SUPPORT, test "x$enable_websocket_support" = "xyes") * \endcode * * \section thread_safety 1.3. noPoll thread safety considerations * * noPoll is designed as a thread agnostic stateless library so it can * fit in any project configuration (no matter if it uses threads or * event notification). * * In the case you are planning to use noPoll in a project that uses * threads and you expect to make calls to the noPoll API from * different threads at the same time, then you must setup four * callbacks that will help noPoll to create, destroy, lock and unlock * mutexes. For that, check documentation about \ref * nopoll_thread_handlers * * \section creating_a_nopoll_ctx 1.4. Creating a noPoll context * * Before working with noPoll API you must create a * \ref noPollCtx object, which represents a single library instance * state. You can create as much \ref noPollCtx objects inside the process as you * want. To create it you must do something like: * * \code * noPollCtx * ctx = nopoll_ctx_new (); * if (! ctx) { * // error some handling code here * } * * // do some WebSocket operations (as client or listener)... * * // ...and once you are done and after terminating all messages and * // connections created you have to release the context by doing the * // following: * nopoll_ctx_unref (ctx); * \endcode * * * * * \section creating_basic_web_socket_server 1.5. Creating a basic WebSocket server with noPoll (using noPoll own loop) * * \note Remember you can see many of the code already supported by noPoll by checking the nopoll regression listener at: https://dolphin.aspl.es/svn/publico/nopoll/trunk/test/nopoll-regression-listener.c * * Now let's see how to create a simple WebSocket server using noPoll own loop: * \code * // create a listener to receive connections on port 1234 * noPollConn * listener = nopoll_listener_new (ctx, "0.0.0.0", "1234"); * if (! nopoll_conn_is_ok (listener)) { * // some error handling here * } * * // now set a handler that will be called when a message (fragment or not) is received * nopoll_ctx_set_on_msg (ctx, listener_on_message, NULL); * * // now call to wait for the loop to notify events * nopoll_loop_wait (ctx, 0); * \endcode * * Now, every time a frame is received, the handler * listener_on_message will be called. Here is an example about * that handler: * * \code * void listener_on_message (noPollCtx * ctx, noPollConn * conn, noPollMsg * msg, noPollPtr * user_data) { * // print the message (for debugging purposes) and reply * printf ("Listener received (size: %d, ctx refs: %d): (first %d bytes, fragment: %d) '%s'\n", * nopoll_msg_get_payload_size (msg), * nopoll_ctx_ref_count (ctx), shown, nopoll_msg_is_fragment (msg), example); * * // reply to the message * nopoll_conn_send_text (conn, "Message received", 16); * * return; * } * \endcode * * \section creating_basic_web_socket_client 1.6. Creating a basic WebSocket client with noPoll * * \note Remember you can see many of the code already supported by noPoll by checking the nopoll regression client at: https://dolphin.aspl.es/svn/publico/nopoll/trunk/test/nopoll-regression-client.c * * The process of creating a WebSocket connection is really * simple. After creating a context (\ref noPollCtx) you connect to * the listener by using: * * \code * // call to create a connection * noPollConn * conn = nopoll_conn_new (ctx, "localhost", "1234", NULL, NULL, NULL, NULL); * if (! nopoll_conn_is_ok (conn)) { * // some error handling here * } * * \endcode * * After that, you can call to \ref nopoll_conn_is_ready to check if * the connection is ready to send and receive content. If you don't * have a loop to wait on, you can use the following function to wait * for the connection to be ready: * * \code * if (! nopoll_conn_wait_until_connection_ready (conn, 5)) { * // some error handling * } * \endcode * * In any case, once the connection is ready, either because \ref * nopoll_conn_is_ready returned \ref nopoll_true or because you used * \ref nopoll_conn_wait_until_connection_ready, you can send a * message by using the following: * * \code * // send a message * if (nopoll_conn_send_text (conn, "Hello there! this is a test", 27) != 27) { * // send a message * } * \endcode * * Now, to receive the content from this connection see the following. * * \section nopoll_manual_reading_content_from_connection 1.7. How to handle read I/O operations on noPollConn objects (or how it integrates with existing I/O loops). * * Now, to receive content from connections (or to handle master listeners requests) you can use the following methods: * * - Use noPoll own I/O loop wait (\ref nopoll_loop_wait) and set a onMessage received handler (\ref nopoll_ctx_set_on_msg and \ref nopoll_conn_set_on_msg) * * - Use your already working I/O loop to wait for changes on the * socket associated to the noPollConn object (see \ref * nopoll_conn_socket). In the case of detecting changes on a master listener connection use to \ref nopoll_conn_accept to accept new incoming connections. In the case of detecting changes in connections with I/O, call to \ref nopoll_conn_get_msg to receive entire messages or \ref nopoll_conn_read (if you want to use streaming API). * * \section nopoll_manual_handling_fragments_with_websocket 1.8 Will noPoll automatically integrate fragments into a single message?. * * Ok, in general it does, however we would have to look into the * particular case to give a correct answer. That's because the term * "fragment" is *really* broad especially for WebSocket. * * In general, you shouldn't design in a way that this is important * for your application to work. All about WebSocket must be designed * as if you were working with a stream oriented socket. * * For example, WebSocket standard states that any intermediary may * split or join frames. And by any intermediary we mean any WebSocket * stack in the middle of the transmission including the sender and * the receiver. * * The practical implication is that if your WebSocket peer sends a * message, it may reach to the other peer consolidated along with * other smaller messages or it may be received as several "complete" * WebSocket frames. * * In general noPoll tries to consolidate internal frame fragments but * only for some particular functions like (\ref * nopoll_conn_get_msg). For example, if a frame fragment is received, * that is, just the header and part of the body, this particular * function will "wait" until the entire body is received. And by * "wait" we mean the function will return NULL, waiting you to call * again in the future to get the entire message. * * In general, even though the standard allows it (as we saw), noPoll * doesn't do any split or join operation to avoid confusion. However, * some API calls like \ref nopoll_conn_read, which provides an "stream * view" of the connection, will serve bytes as they come (so the * frame concept is not present in this case). * * \section nopoll_manual_retrying_write_operations 2.1. Retrying failed write operations * * Every time you do a write operation (using for example \ref * nopoll_conn_send_text or \ref nopoll_conn_send_text_fragment) there * is a possibility that the write operation failes because the socket * isn't capable to keep on accepting more data. * * In that case, errno == 11 (or \ref NOPOLL_EWOULDBLOCK) is returned * so you can check this to later retry the write operation. * * Because websocket involves sending headers that already includes * the size of the message sent, you can't just retry by calling again * to the send operation used (like \ref nopoll_conn_send_text), * especially because you must "continue" the send operation where it * was left instead of sending more content with additional * headers. In short, you must complete the operation. * * To this end, you must use the following functions to check and * complete pending write operations: * * - \ref nopoll_conn_pending_write_bytes * - \ref nopoll_conn_complete_pending_write * * Here is a posible complete function considering all points: * * \code * int websocket_write (noPollConn * conn, const char * content, int length) { * // FIRST PART: normal send operation * int tries = 0; * int bytes_written; * * // do write operation and check * bytes_written = nopoll_conn_send_text (conn, content, length); * if (bytes_written == length) { * // operation completed, just return bytes written * return bytes_written; * } * * // SECOND PART: retry in the case of failure * // some failure found, check errno * while (tries < 5 && errno == NOPOLL_EWOULDBLOCK && nopoll_conn_pending_write_bytes (conn) > 0) { * // ok, unable to write all data but that data is waiting to be flushed * // you can return here and then make your application to retry again or * // try it right now, but with a little pause before continue * nopoll_sleep (10000); // lets wait 10ns * * // flush and check if write operation completed * if (nopoll_conn_complete_pending_write (conn) == 0) * return length; * * // limit loop * tries++; * } * * // failure, return error code reported by the first call or the last retry * return bytes_written; * } * \endcode * * As we can see, the example tries to first write the content and * then check for errors, trying to complete write in the case of * errno == NOPOLL_EWOULDBLOCK, but, before going ahead retrying, the * function sleeps a bit. * * A very important note to consider is that this isn't by far * the best way to do this. This example is just to demonstrate the * concept. The "ideal" implementation would be not to do any retry * here (second part) but let the engine looping and waiting for this * WebSocket to retry later, letting the overall application to keep * on doing other things meanwhile (like writing or handling I/O in other * connections) rather than locking the caller (as the example do). * * Knowing this, if you want a ready to use function that implements * concept (for the second part), you can directly use: * * - \ref nopoll_conn_flush_writes * * With it, a fairly complete and efficient write operation would be: * * \code * // do write operation * bytes_written = nopoll_conn_send_text (conn, content, length); * * // complete pending write by flushing and limitting operation for 2 seconds * // pass to the function bytes_written as returned by nopoll_conn_send_text * bytes_written = nopoll_conn_flush_writes (conn, 2000000, bytes_written); * * \endcode * * \section nopoll_implementing_port_sharing 2.2. Implementing protocol port sharing: running WebSocket and legacy protocol on the same port * * Current noPoll design allows to implement full WebSocket connection * accept by calling to \ref nopoll_conn_accept but also it is * possible to let other application to do the accept connection, try * to guess if what is comming is a WebSocket connection, to then let * noPoll to complete the accept socket operation. * * As a example, these concepts are being implemented by Vortex * Library (http://www.aspl.es/vortex) to allow running on the same * port BEEP and BEEP over WebSocket. * * The way each application implements "port sharing concept" is * really especific to each case, but here are the general steps: * * - 1. Let the legacy application to accept the socket by the standard call accept () * * - 2. Then, call to recv (socket, buffer[3], 3, MSG_PEEK); to get just 3 bytes from the socket without removing them from the queue. * * - 3. Then check with something like the following to know if the incoming connection seems to be a WebSocket one or not: * * \code * // detect tls conn * nopoll_bool is_tls_conn = bytes[0] == 22 && bytes[1] == 3 && bytes[2] == 1; * * // detect then both values (TLS WebSocket and just WebScoket) * if (! axl_memcmp ("GET", bytes, 3) && ! is_tls_conn) * return nopoll_false; // nothing detected here (it doesn't seems * // to be a websocket connection) continue as normal * * // nice, it seems we've found an incoming WebSocket connection * \endcode * * - 4. In the case nothing was detected, stop, and continue with the * usual accept process that was already in place. In the case you * detected a posible WebSocket connection, then, you'll have to * do something like: * * \code * // Create a noPollConn listener object that presents the legacy listener where the connection was * // received. In general is recommended to reuse these references to avoid creating over and over * // again new references as new WebSocket connections are received. * // * noPollConn * nopoll_listener = nopoll_listener_from_socket (nopoll_ctx, listener_socket); * * // Create an accepted listener connection reusing the socket received * // where nopoll_ctx is a reference to a noPollCtx object created and reused by all * // connections accepted, and _socket represents the socket file descriptor * // * noPollConn * conn = nopoll_listener_from_socket (nopoll_ctx, _socket); * if (! nopoll_conn_is_ok (conn)) { * // ok, something failed, release and return * nopoll_conn_close (conn); * return; * } * * // Now, initiate the entire WebSocket accept process (including TLS one) * // where nopoll_ctx is the context we used before, nopoll_listener is a listener where * // the connection was received, conn is the connection accepted and is_tls_conn * // is an indication about what to expect about TLS process. * // * if (! nopoll_conn_accept_complete (nopoll_ctx, nopoll_listener, conn, _socket, is_tls_conn)) { * // failed to accept connection, release connection * nopoll_conn_close (conn); * // optionally close listener reference if it is not reused * nopoll_conn_close (nopoll_listener); * return; * } * * // now process incoming messages as configured (either because you've configured an onMessage handler) * // or because you are handling directly all incoming content (streaming API). * \endcode * * \section nopoll_implementing_mutual_auth 3.1. Implementing mutual TLS certificate verification * * In the case you want to verify provided client certificate at the * server you can use the following code. It optionally provides the * CA (as root.pem) which will make the listener to also verify client * matches that CA. However, if you remove that CA (root.pem) your * server will just verify peer's certificate: * * \code * // configure server certificates (server.pem) signed by the * // provided ca (root.pem) also configured in the last * // parameter * if (! nopoll_conn_opts_set_ssl_certs (opts, * "server.pem", * "server.key", * NULL, * "root.pem")) { * printf ("ERROR: unable to setup certificates...\n"); * return -1; * } * * // configure peer verification * nopoll_conn_opts_ssl_peer_verify (opts, nopoll_true); * * // create listener * listener2 = nopoll_listener_tls_new_opts (ctx, opts, "0.0.0.0", "1239"); * if (! nopoll_conn_is_ok (listener2)) { * printf ("ERROR: Expected to find proper listener TLS connection status (:1236, SSLv23), but found..\n"); * return -1; * } * \endcode * * Here the important part is calling to \ref * nopoll_conn_opts_ssl_peer_verify. This is because peer verification * is disabled for servers (that is, servers by default do not verify * peer's certificate which usually is not provided). However, this is * not true for clients which is by default enabled (of course, client * must always verify server's certificate when connecting with * TLS/SSL, was we have said, this can be controlled by \ref * nopoll_conn_opts_ssl_peer_verify too). * * Now, in the case you want to provide client certificate for a * WebSocket connecting node, use the following: * * * \code * // create connection options * opts = nopoll_conn_opts_new (); * nopoll_conn_opts_set_ssl_certs (opts, * // certificate * "client.pem", * // private key * "client.pem", * NULL, * // ca certificate * "root.pem"); * * // connect to remote WebSocket server * conn = nopoll_conn_tls_new (ctx, opts, "server-address", "1239", NULL, NULL, NULL, NULL); * \endcode * * \section nopoll_implementing_tls_extended_validation_post_check 3.2. Doing extended validation as a post check task (TLS/SSL) * * The idea is to configure the following handler at your server/listener component: * * \code * nopoll_ctx_set_post_ssl_check (ctx, your_post_ssl_check_handler, ); * \endcode * * Now, somewhere, that handler will have the following signature and code example: * * \code * nopoll_bool your_post_ssl_check_handler (noPollCtx * ctx, * noPollConn * conn, * noPollPtr SSL_CTX, * noPollPtr SSL, * noPollPtr user_data) * { * * // Do here some additional checks on the certificate received (using SSL_CTX and SSL). * // I the case of error, return nopoll_false to ensure the connection is not accepted. * * return nopoll_true; // to accept connection * } * \endcode * * For example, to get the certificate that is proposing the client you could use: * * \code * cert = SSL_get_peer_certificate (SSL); * \endcode * * \section nopoll_implementing_tls_context_creator 3.3. Creating TLS/SSL context to implement especific validation options * * In the case you want to create the SSL_CTX/SSL object so it uses * certain configurations like chain certificates, etc, so that the * verification will only work when matching these settings. * * If this is the case you'll have to use a different handler that * will help noPoll engine to create the SSL context with the settings * you want. By default, the SSL context is created with default * settings if no handler is provided. * * In the case you want to provide a ssl context creator, use: * * \code * nopoll_ctx_set_ssl_context_creator (ctx, your_ssl_context_creator, NULL); * \endcode * * And your handler will have the following signature with a very * basic configuration: * * \code * SSL_CTX * your_ssl_context_creator (noPollCtx * ctx, noPollConn * conn, noPollConnOpts * opts, nopoll_bool is_client, noPollPtr user_data) * { * // very basic context creation using default settings provided by OpenSSL * return SSL_CTX_new (is_client ? TLSv1_client_method () : TLSv1_server_method ()); * } * \endcode * * As you can see, the function must return an SSL_CTX for every * connection received and attempting to start TLS session. * * */ /** * \page nopoll_android_usage Android noPoll's usage * * * \section nopoll_android_usage Using noPoll's Android install and development kit * * Inside http://www.aspl.es/nopoll/downloads/android you'll find two types of downloads. * One prefixed with -install.zip that provides just those binaries needed and -full.zip * that provides a ready to use binaries with all the headers, etc needed to produce * Android solutions using noPoll library. * * * \section nopoll_general_notes General notes * * The idea behind these installers is the following. Because there * are several platforms (android APIs) that you might want to support * and for each platform there could be several archs (up to 6), these * installers include all files compiled for each possible * combination. * * That way, you only have to pick desired combinations to compile * your project with ready to use binaries. * * * \section nopoll_platforms_archs Platforms and archs * * In particular the following platforms are supported by the * NDK. Each of them represents an android particular version: * * - android-12 * - android-13 * - android-14 * - android-15 * - android-16 * - android-17 * - android-18 * - android-19 * - android-21 * - android-3 * - android-4 * - android-5 * - android-8 * - android-9 * * In the following link you have a declaration between each platform * and each Android version: https://developer.android.com/ndk/guides/stable_apis.html * * Now, for each platform you may have different archs supported (cpu * style): * * - arm * - arm64 * - mips * - mips64 * - x86 * - x86_64 * * More information about this at: https://developer.android.com/ndk/guides/abis.html * * \section nopoll_using_install_bundle Using the install bundle * * Assuming previous information, please, uncompress * nopoll-VERSION-instal.zip bundle. Inside it, you'll find a * "install" folder that inside includes the following structure: * * \code * install//lib//{libfiles}.so * \endcode * * That way, if you need ready to use compiled libraries for android-21, arch mips64, the look at: * * \code * install/android-21//lib/mips64/{libfiles}.so files. * \endcode * * You might wonder why don't use a //lib * scheme? That's good question. This is because Android * architectural design. See "Native code in app packages" in the * following link https://developer.android.com/ndk/guides/abis.html * to know more about this structure. * * The idea is that you have to support all for a given * (android-21 i.e.). * * In that case, the install.zip is prepared to pick the entire * directory content of a given android platform (for example * install/android-21/) so the structure follows the guide lines of * Android but also provides you full support for all archs, in that * platform, to all components that noPoll is made of. * * \section nopoll_use_development_kit Using development kit bundle (full.zip) * * Ok, now for compiling your project for android using this bundle, * please grab a reference to the full installer and uncompress it. * * Inside, you'll find the following structure (more handy for a * developer using autoconf or cmake): * * \code * full///{ready to use devel files to compile using noPoll} * \endcode * * * Now, assuming you are working with an ARM device, running android * 4.0, then you can use files found at: * * \code * full/arm/android-14/bin/ * include/ * lib/ * share/ * ssl/ * \endcode * * * In your case, you only have to provide the following gcc flags to * your cmake or autoconf environment as follows: * * 1) Compiler flags: * * \code * CFLAGS="-I/full/arm/android-14/include -I/full/arm/android-14/include/vortex -I/full/arm/android-14/include/axl -I/full/arm/android-14/include/nopoll" * \endcode * * 2) Linker flags: * * \code * LDFLAGS=" -L/full/arm/android-14/lib -lvortex -lvortex-tls-1.1 -l axl -lssl -lcrypto -lpthread -pthread -lm" * \endcode * * 3) And your compiler must match the target platform, for example, for ARM: * * \code * CC := $(ANDROID_NDK_BIN)/arm-linux-androideabi-gcc * CPP := $(ANDROID_NDK_BIN)/arm-linux-androideabi-g++ * AR := $(ANDROID_NDK_BIN)/arm-linux-androideabi-ar * LD := $(ANDROID_NDK_BIN)/arm-linux-androideabi-ld * \endcode * * After that, according to your compiling tool, have it to use these * indicatations to compile your source code using noPoll. * * */ /** * \page license noPoll License * * \section licence_intro noPoll terms of use * The noPoll is release under the terms of the Lesser General * Public license (LGPL). You can find it at * http://www.gnu.org/licenses/licenses.html#LGPL. * * The main implication this license has is that you are allowed to * use the noPoll for commercial application as well on open * source application using GPL/LGPL compatible licenses. * * Restrictions for proprietary development are the following: * * - You have to provide back to the main repository or to your * customers any change, modifications, improvements you may do to the * noPoll. Of course, we would appreciate to add to the main * noPoll repository any patch, idea or improvement you send * us but you are not required to do so. * * - You cannot use our company name or image, the noPoll name * or any trademark associated to that product or any product the * Advanced Software Production Line company may have in a way that * they could be damaged. Of course, we would appreciate your project, in * the case it is a proprietary one, make a reference to us but you are * not required to do so. * * \section license_about_static_linking About statically linking noPoll * * Statically linking noPoll or any other component based on GPL/LGPL * is strictly forbidden by the license unless all components * taking part into the final products are all of them GPL, LGPL, MIT, * Bsds, etc, or similar licenses that allow an end user or the * customer to download the entire product source code and clear * instructions to rebuild it. * * If the library is being used by a proprietary product the only * allowed option is dynamic linking (so final user is capable of * updating that dynamic linked part) or a practical procedure where * the propritary binary object along with the instructions to relink * the LGPL part (including an update or modification of it) is * provided. * * An end user or customer using a product using LGPL components must * be able to rebuild those components by introducing updates or * improvements. * * Thus, statically linking a LGPL components without considering * previous points takes away this user/customer right because he/she * cannot replace/update that LGPL component anymore unless you can * have access to the whole solution. * * - See more about this at: https://www.gnu.org/licenses/lgpl-2.1.html * - Also at: http://stackoverflow.com/questions/10130143/gpl-lgpl-and-static-linking * - LGPL 2.1 Guide: http://copyleft.org/guide/comprehensive-gpl-guidech11.html * * \section other Contact us to know more about licenses. * * Use the following contact information to reach us about this issue. * * \code * Postal address: * Advanced Software Production Line, S.L. * C/ Antonio Suarez Nº 10, * Edificio Alius A, Despacho 102 * Alcalá de Henares 28802 (Madrid) * Spain * * Email address: * info@aspl.es - http://www.aspl.es/nopoll * Fax and Telephones: * (+34) 91 669 55 32 - (+34) 91 231 44 50 * From outside Spain must use (+34) prefix. * \endcode * * */