├── src ├── version.rc ├── log_eventlog_messages.mc ├── rand.c ├── conn_wsa_errno.c ├── pearson.c ├── regex.c ├── thread_posix.c ├── thread_win.c ├── CMakeLists.txt ├── digest.c ├── mutex_win.c ├── proxy_client.c ├── mutex_posix.c └── worker.c ├── .gitignore ├── doc ├── icons │ ├── 24x24.png │ ├── 256x256.png │ ├── 48x48.png │ ├── 96x96.png │ ├── windows.ico │ ├── installer.bmp │ ├── CMakeLists.txt │ └── scalable.svg ├── hhc.cmd.in ├── openelp.service.in ├── openelp.xml ├── openelpd.1 ├── ELProxy.conf └── CMakeLists.txt ├── .gitattributes ├── AUTHORS ├── include ├── CMakeLists.txt ├── md5.h ├── conn_wsa_errno.h ├── pearson.h ├── log_syslog.h ├── rand.h ├── log_eventlog.h ├── conf.h ├── digest.h ├── regex.h ├── thread.h ├── registration.h ├── proxy_msg.h ├── proxy_client.h ├── worker.h ├── log.h ├── proxy_conn.h ├── mutex.h └── conn.h ├── cmake ├── unix2dos.cmake ├── BundlePCRE.cmake └── Package.cmake ├── TODO.md ├── CONTRIBUTING.md ├── LICENSE ├── .github └── workflows │ ├── release.yaml │ └── ci.yaml ├── test ├── CMakeLists.txt ├── test_digest.c ├── test_proxy.c ├── test_md5.c ├── test_e2e.c ├── test_conn.c └── test_regex.c ├── README.md └── CMakeLists.txt /src/version.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cottsay/openelp/HEAD/src/version.rc -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | /.vs 3 | /CMakeSettings.json 4 | /ELProxy.conf 5 | /build 6 | /out 7 | -------------------------------------------------------------------------------- /doc/icons/24x24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cottsay/openelp/HEAD/doc/icons/24x24.png -------------------------------------------------------------------------------- /doc/icons/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cottsay/openelp/HEAD/doc/icons/256x256.png -------------------------------------------------------------------------------- /doc/icons/48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cottsay/openelp/HEAD/doc/icons/48x48.png -------------------------------------------------------------------------------- /doc/icons/96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cottsay/openelp/HEAD/doc/icons/96x96.png -------------------------------------------------------------------------------- /doc/icons/windows.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cottsay/openelp/HEAD/doc/icons/windows.ico -------------------------------------------------------------------------------- /doc/icons/installer.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cottsay/openelp/HEAD/doc/icons/installer.bmp -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /cmake/*.patch text eol=lf 2 | *.c linguist-language=C 3 | *.h linguist-language=C 4 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Author and Maintainer 2 | --------------------- 3 | Scott K Logan 4 | 5 | Contributors 6 | ------------ 7 | Alexander Peslyak 8 | -------------------------------------------------------------------------------- /include/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Install 3 | # 4 | 5 | install(DIRECTORY ${OPENELP_INCLUDE_DIR}/openelp 6 | DESTINATION "${INCLUDE_INSTALL_DIR}" 7 | COMPONENT devel 8 | FILES_MATCHING PATTERN "*.h" 9 | ) 10 | -------------------------------------------------------------------------------- /doc/hhc.cmd.in: -------------------------------------------------------------------------------- 1 | @"@OPENELP_DOC_HTMLHELP_PATH_BS@" %* 2 | 3 | @IF %ERRORLEVEL% EQU 0 GOTO DONE 4 | 5 | @IF EXIST "@OPENELP_DOC_HTMLHELP_OUT_PATH_BS@" GOTO DONE 6 | 7 | @ECHO Expected .chm file was not generated! 8 | @EXIT /B 1 9 | 10 | :DONE 11 | 12 | @EXIT /B 0 13 | -------------------------------------------------------------------------------- /doc/openelp.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Open Source EchoLink Proxy 3 | After=network.target nss-lookup.target 4 | ConditionPathExists=@FULL_SYSCONF_INSTALL_DIR@/ELProxy.conf 5 | 6 | [Service] 7 | Type=forking 8 | ExecStart=@FULL_BIN_INSTALL_DIR@/openelpd -q -S @FULL_SYSCONF_INSTALL_DIR@/ELProxy.conf 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /cmake/unix2dos.cmake: -------------------------------------------------------------------------------- 1 | if(NOT ${CMAKE_ARGC} EQUAL 5) 2 | message(FATAL_ERROR 3 | "Usage: cmake -P unix2dos.cmake " 4 | ) 5 | endif() 6 | 7 | file(READ ${CMAKE_ARGV3} FILE_CONTENTS) 8 | 9 | string(REGEX REPLACE "(^|[^\r])\n($|[^\r])" "\\1\r\n\\2" 10 | FILE_CONTENTS_FIXED 11 | ${FILE_CONTENTS} 12 | ) 13 | 14 | file(WRITE ${CMAKE_ARGV4} ${FILE_CONTENTS_FIXED}) 15 | -------------------------------------------------------------------------------- /doc/openelp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Open Source EchoLink Proxy 4 | OpenELP is an open source EchoLink proxy for Linux and Windows. Enable this option, if you are providing a EchoLink proxy server. You need the openelp package installed for this option to be useful. 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | Todo Items 2 | ========== 3 | 4 | Features 5 | -------- 6 | * More args for Windows service 7 | * Publish to statsd 8 | * Provide fail2ban filter 9 | * Support pause/continue in Windows service 10 | * Support socket activation in systemd 11 | * Support CIDR notation for AdditionalExternalBindAddresses 12 | 13 | Optimizations 14 | ------------- 15 | * Re-use same slot on reconnect 16 | * Skip unnecessary registration updates 17 | * Stop allocating new memory after proxy\_start 18 | 19 | Additional Settings 20 | ------------------- 21 | * Timeout (ConnectionTimeout in config) 22 | * Inactivity timeout 23 | * TCP connection whitelisting 24 | * TCP Retry Count 25 | * Max data packet size 26 | * Buffer sizes 27 | * Configurable SO\_KEEPALIVE 28 | * Configurable SO\_REUSEADDR 29 | * Allow/reject duplicate callsigns 30 | -------------------------------------------------------------------------------- /src/log_eventlog_messages.mc: -------------------------------------------------------------------------------- 1 | MessageIdTypedef=DWORD 2 | 3 | SeverityNames=(Informational=0x1:STATUS_SEVERITY_INFORMATIONAL 4 | Warning=0x2:STATUS_SEVERITY_WARNING 5 | Error=0x3:STATUS_SEVERITY_ERROR) 6 | 7 | LanguageNames=(English=0x409:MSG00409) 8 | 9 | MessageId=0x0 10 | Severity=Error 11 | Facility=Application 12 | SymbolicName=LOG_IDENT_FATAL 13 | Language=English 14 | %1 15 | . 16 | 17 | MessageId=0x1 18 | Severity=Error 19 | Facility=Application 20 | SymbolicName=LOG_IDENT_ERROR 21 | Language=English 22 | %1 23 | . 24 | 25 | MessageId=0x2 26 | Severity=Warning 27 | Facility=Application 28 | SymbolicName=LOG_IDENT_WARN 29 | Language=English 30 | %1 31 | . 32 | 33 | MessageId=0x3 34 | Severity=Informational 35 | Facility=Application 36 | SymbolicName=LOG_IDENT_INFO 37 | Language=English 38 | %1 39 | . 40 | 41 | MessageId=0x4 42 | Severity=Informational 43 | Facility=Application 44 | SymbolicName=LOG_IDENT_DEBUG 45 | Language=English 46 | %1 47 | . 48 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | Contributions to OpenELP are welcome from anyone in the form of a pull request 5 | on GitHub. If you decide to contribute to OpenELP be sure to add yourself to 6 | the `AUTHORS` file in the project root. 7 | 8 | Before issuing a PR 9 | ------------------- 10 | * Add any appropriate tests to the `tests/` directory. 11 | * Run `make doc` and fix any Doxygen warnings 12 | * Verify that your code's style aligns with the rest of the project. 13 | * Squash your commits so that each commit deals with exactly one feature or 14 | issue. 15 | 16 | Code Format 17 | ----------- 18 | * All functions should have a prototype in either an appropriate header file, 19 | or at the beginning of the source file. This includes static functions. 20 | * All functions should be listed and implemented alphabetically. 21 | * All functions should have Doxygen documentation on the prototype which 22 | includes at least a `brief`, `param` where appropriate and `returns` where 23 | appropriate. 24 | 25 | Important Links 26 | --------------- 27 | * [OpenELP authors list](https://raw.githubusercontent.com/cottsay/openelp/main/AUTHORS) 28 | * [OpenELP license](https://raw.githubusercontent.com/cottsay/openelp/main/LICENSE) 29 | * [OpenELP bug tracker](https://github.com/cottsay/openelp/issues) 30 | * [EchoLink proxy information](http://www.echolink.org/proxy.htm) 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Scott K Logan. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | 3. Neither the name of the copyright holder nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /include/md5.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. 3 | * MD5 Message-Digest Algorithm (RFC 1321). 4 | * 5 | * Homepage: 6 | * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 7 | * 8 | * Author: 9 | * Alexander Peslyak, better known as Solar Designer 10 | * 11 | * This software was written by Alexander Peslyak in 2001. No copyright is 12 | * claimed, and the software is hereby placed in the public domain. 13 | * In case this attempt to disclaim copyright and place the software in the 14 | * public domain is deemed null and void, then the software is 15 | * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the 16 | * general public under the following terms: 17 | * 18 | * Redistribution and use in source and binary forms, with or without 19 | * modification, are permitted. 20 | * 21 | * There's ABSOLUTELY NO WARRANTY, express or implied. 22 | * 23 | * See md5.c for more information. 24 | */ 25 | 26 | #ifdef HAVE_OPENSSL 27 | #include 28 | #elif !defined(_MD5_H) 29 | #define _MD5_H 30 | 31 | /* Any 32-bit or wider unsigned integer data type will do */ 32 | typedef unsigned int MD5_u32plus; 33 | 34 | typedef struct { 35 | MD5_u32plus lo, hi; 36 | MD5_u32plus a, b, c, d; 37 | unsigned char buffer[64]; 38 | MD5_u32plus block[16]; 39 | } MD5_CTX; 40 | 41 | extern void MD5_Init(MD5_CTX *ctx); 42 | extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); 43 | extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: OpenELP Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - release/* 7 | 8 | jobs: 9 | build_windows_installer: 10 | name: Build Windows Installer 11 | runs-on: windows-latest 12 | 13 | steps: 14 | - name: Cache PCRE2 15 | uses: actions/cache@v4 16 | with: 17 | path: ${{runner.workspace}}\build\pcre-prefix\src\pcre2-10.42.tar.gz 18 | key: pcre2-10.42 19 | - name: Clone project 20 | uses: actions/checkout@v4 21 | - name: Create build environment 22 | shell: bash 23 | working-directory: ${{runner.workspace}} 24 | run: mkdir -p build 25 | - name: Configure project 26 | working-directory: ${{runner.workspace}}\build 27 | run: > 28 | cmake ${{github.workspace}} 29 | -Werror=dev 30 | -Werror=deprecated 31 | -DCMAKE_BUILD_TYPE=Release 32 | -DBUILD_TESTING:BOOL=OFF 33 | -DBUILD_SHARED_LIBS:BOOL=ON 34 | -DCMAKE_PROGRAM_PATH:PATH="${{runner.workspace}}\doxygen" 35 | -DCMAKE_C_FLAGS="/WX /experimental:external /external:anglebrackets /external:W0" 36 | -DCMAKE_EXE_LINKER_FLAGS="/WX" 37 | -DCMAKE_SHARED_LINKER_FLAGS="/WX" 38 | -DCMAKE_STATIC_LINKER_FLAGS="/WX" 39 | -DOPENELP_DOC_HTMLHELP:BOOL=OFF 40 | - name: Build PCRE2 41 | run: cmake --build ${{runner.workspace}}\build --config Release -j2 -t pcre 42 | - name: Build project 43 | run: cmake --build ${{runner.workspace}}\build --config Release -j2 44 | - name: Package project 45 | run: cmake --build ${{runner.workspace}}\build --config Release -j2 -t package 46 | - name: Upload package 47 | uses: actions/upload-artifact@v4 48 | with: 49 | name: OpenELP-win64 50 | path: ${{runner.workspace}}\build\OpenELP-*-win64.exe 51 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | macro(add_openelp_exe exe_name) 2 | add_executable(${exe_name} ${ARGN} $) 3 | 4 | get_target_property(OPENELP_INCLUDE_DIRECTORIES openelp_objects INCLUDE_DIRECTORIES) 5 | target_include_directories(${exe_name} PRIVATE ${OPENELP_INCLUDE_DIRECTORIES}) 6 | 7 | get_target_property(OPENELP_LIBRARIES openelp LINK_LIBRARIES) 8 | target_link_libraries(${exe_name} ${OPENELP_LIBRARIES}) 9 | 10 | if(WIN32) 11 | target_compile_options(${exe_name} PRIVATE 12 | "-DOPENELP_API=__declspec(dllexport)" 13 | ) 14 | endif() 15 | endmacro() 16 | 17 | macro(add_openelp_test test_name) 18 | add_openelp_exe(${test_name} ${ARGN}) 19 | 20 | add_test(NAME ${test_name} COMMAND ${test_name}) 21 | 22 | if(WIN32) 23 | set(PATH_SEPARATOR "\\;") 24 | else() 25 | set(PATH_SEPARATOR ":") 26 | endif() 27 | set(TEST_ENVIRONMENT 28 | "PATH=$>;$ENV{PATH},${PATH_SEPARATOR}>" 29 | "LD_LIBRARY_PATH=$>;$ENV{LD_LIBRARY_PATH},${PATH_SEPARATOR}>" 30 | "DYLD_LIBRARY_PATH=$>;$ENV{DYLD_LIBRARY_PATH},${PATH_SEPARATOR}>" 31 | ) 32 | 33 | set_tests_properties( 34 | ${test_name} 35 | PROPERTIES ENVIRONMENT "${TEST_ENVIRONMENT}" TIMEOUT 30) 36 | 37 | list(APPEND OPENELP_TEST_TARGETS ${test_name}) 38 | endmacro() 39 | 40 | add_test(NAME test_exe_help COMMAND $ --help) 41 | add_test(NAME test_exe_invalid COMMAND $ --no-such-option) 42 | set_tests_properties(test_exe_invalid PROPERTIES WILL_FAIL TRUE) 43 | add_test(NAME test_exe_version COMMAND $ --version) 44 | 45 | add_openelp_test(test_conn test_conn.c) 46 | add_openelp_test(test_digest test_digest.c) 47 | add_openelp_test(test_e2e test_e2e.c) 48 | add_openelp_test(test_md5 test_md5.c) 49 | add_openelp_test(test_proxy test_proxy.c) 50 | add_openelp_test(test_regex test_regex.c) 51 | -------------------------------------------------------------------------------- /cmake/BundlePCRE.cmake: -------------------------------------------------------------------------------- 1 | include(ExternalProject) 2 | 3 | cmake_policy(SET CMP0135 OLD) 4 | 5 | set(PCRE_TARGET_VERSION "10.42") 6 | set(PCRE_DIR ${CMAKE_CURRENT_BINARY_DIR}/pcre2) 7 | set(PCRE_C_FLAGS ${CMAKE_C_FLAGS}) 8 | 9 | if(WIN32) 10 | # We don't really care about warnings in PCRE 11 | string(REGEX REPLACE "( |^)/W[0-9]( |$)" "\\1" PCRE_C_FLAGS ${PCRE_C_FLAGS}) 12 | elseif(BUILD_SHARED_LIBS) 13 | set(PCRE_C_FLAGS "${PCRE_C_FLAGS} -fPIC") 14 | endif() 15 | 16 | set(PCRE_CMAKE_ARGS 17 | -DBUILD_SHARED_LIBS:BOOL=OFF 18 | -DBUILD_STATIC_LIBS:BOOL=ON 19 | -DCMAKE_C_FLAGS:STRING=${PCRE_C_FLAGS} 20 | -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} 21 | -DCMAKE_INSTALL_LIBDIR:PATH=lib 22 | -DCMAKE_INSTALL_PREFIX:PATH=${PCRE_DIR} 23 | -DCMAKE_VERBOSE_MAKEFILE:BOOL=${CMAKE_VERBOSE_MAKEFILE} 24 | -DINSTALL_MSVC_PDB:BOOL=OFF 25 | -DPCRE2_BUILD_PCRE2_8:BOOL=ON 26 | -DPCRE2_BUILD_PCRE2_16:BOOL=OFF 27 | -DPCRE2_BUILD_PCRE2_32:BOOL=OFF 28 | -DPCRE2_BUILD_PCRE2GREP:BOOL=OFF 29 | -DPCRE2_BUILD_TESTS:BOOL=OFF 30 | -DPCRE2_SUPPORT_JIT:BOOL=OFF 31 | -DPCRE2_SUPPORT_LIBBZ2:BOOL=OFF 32 | -DPCRE2_SUPPORT_LIBEDIT:BOOL=OFF 33 | -DPCRE2_SUPPORT_LIBREADLINE:BOOL=OFF 34 | -DPCRE2_SUPPORT_VALGRIND:BOOL=OFF 35 | -DPCRE2_SUPPORT_LIBZ:BOOL=OFF 36 | ) 37 | 38 | ExternalProject_Add(pcre 39 | URL "https://github.com/PCRE2Project/pcre2/releases/download/pcre2-${PCRE_TARGET_VERSION}/pcre2-${PCRE_TARGET_VERSION}.tar.gz" 40 | URL_HASH SHA256=c33b418e3b936ee3153de2c61cc638e7e4fe3156022a5c77d0711bcbb9d64f1f 41 | CMAKE_ARGS ${PCRE_CMAKE_ARGS} 42 | ) 43 | 44 | ExternalProject_Get_Property(pcre 45 | SOURCE_DIR 46 | ) 47 | 48 | if(WIN32) 49 | if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") 50 | set(PCRE_POSTFIX "-staticd") 51 | else() 52 | set(PCRE_POSTFIX "-static") 53 | endif() 54 | endif() 55 | 56 | set(OPENELP_PCRE_LICENSE_PATH "${SOURCE_DIR}/LICENCE") 57 | set(PCRE_INCLUDE_DIRS "${PCRE_DIR}/include") 58 | set(PCRE_LIBRARY_DIRS "${PCRE_DIR}/lib") 59 | set(PCRE_LIBRARIES "pcre2-8${PCRE_POSTFIX}") 60 | -------------------------------------------------------------------------------- /include/conn_wsa_errno.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file conn_wsa_errno.h 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Internal API for retrieving WSA errors 45 | */ 46 | 47 | #ifndef CONN_WSA_ERRNO_H_ 48 | #define CONN_WSA_ERRNO_H_ 49 | 50 | /*! 51 | * @brief Retrieves the last WSA error 52 | * 53 | * @returns ERRNO value, or WSA error if conversion failed 54 | */ 55 | int conn_wsa_errno(void); 56 | 57 | #endif /* CONN_WSA_ERRNO_H_ */ 58 | -------------------------------------------------------------------------------- /src/rand.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file rand.c 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Implmentation of the random number generator 45 | */ 46 | 47 | #include 48 | #include 49 | 50 | #include "rand.h" 51 | 52 | int rand_init(void) 53 | { 54 | srand((unsigned int)time(NULL)); 55 | 56 | return 0; 57 | } 58 | 59 | int rand_get(uint32_t *rand_val) 60 | { 61 | *rand_val = rand(); 62 | 63 | return 0; 64 | } 65 | 66 | void rand_free(void) 67 | { 68 | } 69 | -------------------------------------------------------------------------------- /include/pearson.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file pearson.h 3 | * 4 | * @copyright 5 | * Copyright © 2020, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Internal API for random number generation 45 | */ 46 | 47 | #ifndef PEARSON_H_ 48 | #define PEARSON_H_ 49 | 50 | #include 51 | #include 52 | 53 | /*! 54 | * @brief Computes the 8-bit Pearson hash of some data 55 | * 56 | * @param[in] data Buffer containing data to be hashed 57 | * @param[in] data_len Number of bytes in data 58 | * 59 | * @returns 8-bit Pearson hash of the data 60 | */ 61 | uint8_t pearson_get(const uint8_t *data, size_t data_len); 62 | 63 | #endif /* PEARSON_H_ */ 64 | -------------------------------------------------------------------------------- /doc/icons/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Targets 3 | # 4 | 5 | set_directory_properties(PROPERTIES CLEAN_NO_CUSTOM 1) 6 | 7 | add_custom_command(OUTPUT "${OPENELP_DOC_DIR}/icons/24x24.png" 8 | COMMAND convert 9 | ARGS -background none -resize 24x24 10 | "${OPENELP_DOC_DIR}/icons/scalable.svg" 11 | "${OPENELP_DOC_DIR}/icons/24x24.png" 12 | MAIN_DEPENDENCY "${OPENELP_DOC_DIR}/icons/scalable.svg" 13 | ) 14 | add_custom_command(OUTPUT "${OPENELP_DOC_DIR}/icons/48x48.png" 15 | COMMAND convert 16 | ARGS -background none -resize 48x48 17 | "${OPENELP_DOC_DIR}/icons/scalable.svg" 18 | "${OPENELP_DOC_DIR}/icons/48x48.png" 19 | MAIN_DEPENDENCY "${OPENELP_DOC_DIR}/icons/scalable.svg" 20 | ) 21 | add_custom_command(OUTPUT "${OPENELP_DOC_DIR}/icons/96x96.png" 22 | COMMAND convert 23 | ARGS -background none -resize 96x96 24 | "${OPENELP_DOC_DIR}/icons/scalable.svg" 25 | "${OPENELP_DOC_DIR}/icons/96x96.png" 26 | MAIN_DEPENDENCY "${OPENELP_DOC_DIR}/icons/scalable.svg" 27 | ) 28 | add_custom_command(OUTPUT "${OPENELP_DOC_DIR}/icons/256x256.png" 29 | COMMAND convert 30 | ARGS -background none -resize 256x256 31 | "${OPENELP_DOC_DIR}/icons/scalable.svg" 32 | "${OPENELP_DOC_DIR}/icons/256x256.png" 33 | MAIN_DEPENDENCY "${OPENELP_DOC_DIR}/icons/scalable.svg" 34 | ) 35 | add_custom_command(OUTPUT "${OPENELP_DOC_DIR}/icons/installer.bmp" 36 | COMMAND convert 37 | ARGS -background white -alpha remove -alpha off 38 | "${OPENELP_DOC_DIR}/icons/scalable.svg" 39 | -resize x57 -gravity center -extent 150x57 40 | BMP3:"${OPENELP_DOC_DIR}/icons/installer.bmp" 41 | MAIN_DEPENDENCY "${OPENELP_DOC_DIR}/icons/scalable.svg" 42 | ) 43 | add_custom_command(OUTPUT "${OPENELP_DOC_DIR}/icons/windows.ico" 44 | COMMAND convert 45 | "${OPENELP_DOC_DIR}/icons/24x24.png" 46 | "${OPENELP_DOC_DIR}/icons/48x48.png" 47 | "${OPENELP_DOC_DIR}/icons/96x96.png" 48 | "${OPENELP_DOC_DIR}/icons/256x256.png" 49 | "${OPENELP_DOC_DIR}/icons/windows.ico" 50 | DEPENDS 51 | "${OPENELP_DOC_DIR}/icons/24x24.png" 52 | "${OPENELP_DOC_DIR}/icons/48x48.png" 53 | "${OPENELP_DOC_DIR}/icons/96x96.png" 54 | "${OPENELP_DOC_DIR}/icons/256x256.png" 55 | ) 56 | 57 | add_custom_target(icons 58 | DEPENDS 59 | "${OPENELP_DOC_DIR}/icons/24x24.png" 60 | "${OPENELP_DOC_DIR}/icons/48x48.png" 61 | "${OPENELP_DOC_DIR}/icons/96x96.png" 62 | "${OPENELP_DOC_DIR}/icons/256x256.png" 63 | "${OPENELP_DOC_DIR}/icons/installer.bmp" 64 | "${OPENELP_DOC_DIR}/icons/windows.ico" 65 | ) 66 | -------------------------------------------------------------------------------- /include/log_syslog.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file log_syslog.h 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Defenitions and prototypes for interfacing with Syslog 45 | */ 46 | 47 | #ifndef LOG_SYSLOG_H_ 48 | #define LOG_SYSLOG_H_ 49 | 50 | #ifdef HAVE_SYSLOG 51 | # include 52 | #elif !defined(_log_syslog_h) 53 | # define _log_syslog_h 54 | # define LOG_CONS 0 55 | # define LOG_NDELAY 0 56 | # define LOG_DAEMON 0 57 | # define LOG_CRIT 0 58 | # define LOG_ERR 0 59 | # define LOG_WARNING 0 60 | # define LOG_INFO 0 61 | # define LOG_DEBUG 0 62 | # define closelog() 63 | # define openlog(ident, option, facility) return -ENOTSUP 64 | # define vsyslog(facility_priority, format, arglist) 65 | #endif 66 | 67 | #endif /* LOG_SYSLOG_H_ */ 68 | -------------------------------------------------------------------------------- /include/rand.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file rand.h 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Internal API for random number generation 45 | */ 46 | 47 | #ifndef RAND_H_ 48 | #define RAND_H_ 49 | 50 | #include 51 | 52 | /*! 53 | * @brief Frees data allocated by ::rand_init 54 | */ 55 | void rand_free(void); 56 | 57 | /*! 58 | * @brief Initializes the random number generator 59 | * 60 | * @returns 0 on success, negative ERRNO value on failure 61 | */ 62 | int rand_init(void); 63 | 64 | /*! 65 | * @brief Gets a single, 32-bit random number 66 | * 67 | * @param[out] rand_val Random 32-bit value 68 | * 69 | * @returns 0 on success, negative ERRNO value on failure 70 | */ 71 | int rand_get(uint32_t *rand_val); 72 | 73 | #endif /* RAND_H_ */ 74 | -------------------------------------------------------------------------------- /doc/openelpd.1: -------------------------------------------------------------------------------- 1 | .TH OPENELPD 1 2 | .SH NAME 3 | openelpd \- an open source EchoLink proxy daemon 4 | .SH SYNOPSIS 5 | .B openelpd 6 | [\fB\-d\fR] 7 | [\fB\-F\fR] 8 | [\fB\-L\fR ] 9 | [\fB\-S\fR] 10 | [\fB\-V\fR] 11 | [configuration file] 12 | .SH DESCRIPTION 13 | \fBEchoLink\fR is a software system for connecting licensed radio amateurs to communicate over the internet using Voice over IP (VoIP). EchoLink clients require that UDP ports 5198 and 5199 be "forwarded" to the machine running the client. When this is not allowed or otherwise not possible, a proxy is used to listen for data on these ports and forward this data to a client over a TCP stream. 14 | .TP 15 | \fBOpenELP\fR, an Open Source EchoLink Proxy, is a performance-oriented proxy which can be used with EchoLink software. It uses a compatible protocol, and a compatible configuration file syntax with the Java EchoLink proxy available from the EchoLink website. 16 | .SH OPTIONS 17 | .TP 18 | .BR \-d 19 | Enable debugging output to the specified logging utility (by default, this is STDOUT/STDERR). Default behavior is to produce no debug output. 20 | .TP 21 | .BR \-F 22 | If specified, this parameter causes the main thread to run in the foreground, and not daemonize. Child threads are still created for each additional port, but the main thread does not return unless the proxy is shut down. Default behavior is to daemonize. 23 | .TP 24 | .BR \-L 25 | After initial startup is complete, switch the logging utility to output all information to the given file. If the file does not exist, it will be created. If it does exist, new log data will be appended to it. This flag is incompatible with \fB\-S\fR. Default behavior is not to use a log file, and route all information to STDOUT. 26 | .TP 27 | .BR \-S 28 | After initial startup is complete, switch the logging utility to output all information to syslog. This flag is incompatible with \fB\-L\fR. Default behaviour is not to use syslog, and route all information to STDOUT. 29 | .TP 30 | .BR \-V 31 | Print the version of the daemon executable and exit. 32 | .TP 33 | If the configuration file path is not specified, \fBopenelpd\fR will first attempt to open the file named ELProxy.conf in the current working directory. On systems where a global configuration file path hint was specified at compile time, \fBopenelpd\fR will use that configuration path as a last resort. 34 | .SH BUGS 35 | Any bugs should be reported to the project repository at http://github.com/cottsay/openelp/issues 36 | .SH AUTHORS 37 | \fBOpenELP\fR was created by Scott K Logan, KM0H 38 | .TP 39 | See the Authors file included with this program for a full list of authors and contributors. 40 | .TP 41 | EchoLink(r) is a registered trademark of Synergenics, LLC. 42 | .SH SEE ALSO 43 | \fBqtel\fR(1) 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | OpenELP, an Open Source EchoLink Proxy 2 | ====================================== 3 | 4 | OpenELP is an open source EchoLink proxy for Linux and Windows. It aims to be 5 | efficient and maintain a small footprint, while still implementing all of the 6 | features present in the official EchoLink proxy. 7 | 8 | OpenELP also has the ability to bind to multiple network interfaces which are 9 | routed to unique external IP addresses, and therefore is capable of accepting 10 | connections from multiple clients simultaneously. 11 | 12 | [![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/cottsay/openelp/ci.yaml?branch=main&event=push&logo=github)](https://github.com/cottsay/openelp/actions/workflows/ci.yaml?query=branch%3Amain+event%3Apush) [![AUR version](https://img.shields.io/aur/version/openelp?logo=arch%20linux&&logoColor=ffffff)](https://aur.archlinux.org/packages/openelp/) [![EPEL package](https://img.shields.io/fedora/v/openelp/epel8?label=epel&logo=red%20hat)](https://src.fedoraproject.org/rpms/openelp) [![Fedora package](https://img.shields.io/fedora/v/openelp?logo=fedora)](https://src.fedoraproject.org/rpms/openelp) [![Codecov](https://img.shields.io/codecov/c/github/cottsay/openelp/main?logo=codecov&logoColor=ffffff)](https://app.codecov.io/gh/cottsay/openelp/branch/main) 13 | 14 | Prerequisites 15 | ------------- 16 | To build OpenELP you will need: 17 | * [CMake](https://cmake.org/) 18 | * [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/) 19 | * [GCC](https://gcc.gnu.org/) or [Visual Studio](http://aka.ms/vs2015) 20 | 21 | If available, OpenELP can use: 22 | * [PCRE2](http://www.pcre.org/) 23 | * [OpenSSL](https://www.openssl.org/) 24 | * [Doxygen](http://www.doxygen.org/) 25 | 26 | If your system doesn't have PCRE2 development files installed, you have the 27 | option of bundling PCRE2 with OpenELP. To do this, specify 28 | `-DOPENELP_BUNDLE_PCRE:BOOL=ON` when you call `cmake`. CMake will download 29 | the PCRE2 sources automatically and build them into the OpenELP library. 30 | 31 | To create a Windows installer, you will also need to install 32 | [NSIS](http://nsis.sourceforge.net/) 33 | 34 | The only runtime dependency that OpenELP has is on the PCRE2 shared library, 35 | unless PCRE2 was bundled into OpenELP. 36 | 37 | To install these prerequisites on Fedora and RHEL (w/EPEL), run: 38 | ``` 39 | sudo dnf install cmake doxygen gcc pcre2-devel pkgconfig openssl-devel 40 | ``` 41 | 42 | Compiling 43 | --------- 44 | Linux: 45 | 46 | mkdir build && cd build 47 | cmake .. 48 | make 49 | 50 | Windows: 51 | 52 | mkdir build && cd build 53 | cmake .. -DOPENELP_BUNDLE_PCRE:BOOL=ON 54 | devenv openelp.sln /build 55 | 56 | Windows Installer: 57 | 58 | devenv openelp.sln /project PACKAGE /build 59 | 60 | License 61 | ------- 62 | See [LICENSE](./LICENSE) file. 63 | 64 | EchoLink® is a registered trademark of Synergenics, LLC. 65 | 66 | Bugs 67 | ---- 68 | All issues and feature requests should be directed to 69 | [the bug tracker](https://github.com/cottsay/openelp/issues). Please review any 70 | open issues before filing new ones. 71 | 72 | -------------------------------------------------------------------------------- /include/log_eventlog.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file log_eventlog.h 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Defenitions and prototypes for interfacing with the Windows Event Log 45 | */ 46 | 47 | #ifndef LOG_EVENTLOG_H_ 48 | #define LOG_EVENTLOG_H_ 49 | 50 | #ifdef HAVE_EVENTLOG 51 | # include 52 | # include 53 | # include "log_eventlog_messages.h" 54 | # define EVENTLOG_ERRNO (-(int64_t)GetLastError()) 55 | typedef HANDLE EVENTLOG_HANDLE; 56 | #else 57 | # define DeregisterEventSource(hEventLog) 58 | # define RegisterEventSource(lpUNCServerName, lpSourceName) NULL 59 | # define ReportEvent(handle, type, cat, eventId, userSid, numStrings, \ 60 | dataSize, strings, rawData) ((void)handle, (void)type, (void)cat, \ 61 | (void)eventId, (void)userSid, (void)numStrings, (void)dataSize, \ 62 | (void)strings, (void)rawData) 63 | # define EVENTLOG_ERRNO -ENOTSUP 64 | # define EVENTLOG_ERROR_TYPE 0 65 | # define EVENTLOG_WARNING_TYPE 0 66 | # define EVENTLOG_INFORMATION_TYPE 0 67 | # define LOG_IDENT_FATAL 0 68 | # define LOG_IDENT_ERROR 0 69 | # define LOG_IDENT_WARN 0 70 | # define LOG_IDENT_INFO 0 71 | # define LOG_IDENT_DEBUG 0 72 | typedef void *EVENTLOG_HANDLE; 73 | #endif 74 | 75 | #endif /* LOG_EVENTLOG_H_ */ 76 | -------------------------------------------------------------------------------- /include/conf.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file conf.h 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Internal API for configuration values 45 | */ 46 | 47 | #ifndef CONF_H_ 48 | #define CONF_H_ 49 | 50 | #include 51 | #include 52 | 53 | #ifndef _WIN32 54 | # include 55 | #endif 56 | 57 | #include "openelp/openelp.h" 58 | #include "log.h" 59 | 60 | /*! 61 | * @brief Frees data allocated by ::conf_init 62 | * 63 | * @param[in,out] conf Target configuration instance 64 | */ 65 | void conf_free(struct proxy_conf *conf); 66 | 67 | /*! 68 | * @brief Initializes the private data in a ::proxy_conf 69 | * 70 | * @param[in,out] conf Target configuration instance 71 | * 72 | * @returns 0 on success, negative ERRNO value on failure 73 | */ 74 | int conf_init(struct proxy_conf *conf); 75 | 76 | /*! 77 | * @brief Parses the values from the given file into the given configuration 78 | * 79 | * @param[in] file Null-terminated string containing the path to the config 80 | * @param[in,out] conf Target configuration instance 81 | * @param[in,out] log Handle for reporting logging events 82 | * 83 | * @returns 0 on success, negative ERRNO value on failure 84 | */ 85 | int conf_parse_file(const char *file, struct proxy_conf *conf, 86 | struct log_handle *log); 87 | 88 | #endif /* CONF_H_ */ 89 | -------------------------------------------------------------------------------- /src/conn_wsa_errno.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file conn_wsa_errno.c 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief WSA error conversion implementation 45 | */ 46 | 47 | #include 48 | 49 | #include 50 | 51 | #include "conn_wsa_errno.h" 52 | 53 | /*! WSA error value coversion table */ 54 | static const int wsa_errno[68] = { 55 | 10000, EPERM, ENOENT, ESRCH, 56 | EINTR, EIO, ENXIO, E2BIG, 57 | ENOEXEC, EBADF, ECHILD, EAGAIN, 58 | ENOMEM, EACCES, EFAULT, 10015, 59 | EBUSY, EEXIST, EXDEV, ENODEV, 60 | ENOTDIR, EISDIR, 10022, ENFILE, 61 | EMFILE, ENOTTY, 10026, EFBIG, 62 | ENOSPC, ESPIPE, EROFS, EMLINK, 63 | 10032, 10033, 10034, EWOULDBLOCK, 64 | EINPROGRESS, EALREADY, ENOTSOCK, EDESTADDRREQ, 65 | EMSGSIZE, EPROTOTYPE, ENOPROTOOPT, EPROTONOSUPPORT, 66 | 10044, EOPNOTSUPP, 10046, EAFNOSUPPORT, 67 | EADDRINUSE, EADDRNOTAVAIL, ENETDOWN, ENETUNREACH, 68 | ENETRESET, ECONNABORTED, ECONNRESET, ENOBUFS, 69 | EISCONN, ENOTCONN, 10058, 10059, 70 | ETIMEDOUT, ECONNREFUSED, ELOOP, ENAMETOOLONG, 71 | 10064, EHOSTUNREACH, ENOTEMPTY, 10067, 72 | }; 73 | 74 | int conn_wsa_errno(void) 75 | { 76 | int ret = WSAGetLastError(); 77 | 78 | if (ret >= 10000 && ret <= 10067) 79 | return wsa_errno[ret - 10000]; 80 | 81 | return ret; 82 | } 83 | -------------------------------------------------------------------------------- /include/digest.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file digest.h 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Internal API for digest generation 45 | */ 46 | 47 | #ifndef DIGEST_H_ 48 | #define DIGEST_H_ 49 | 50 | #include 51 | 52 | #ifndef _WIN32 53 | # include 54 | #endif 55 | 56 | /*! Length in bytes of all digests */ 57 | #define DIGEST_LEN 16 58 | 59 | /*! 60 | * @brief Calculates the digest of the given data 61 | * 62 | * @param[in] data The data whose digest is calculated 63 | * @param[in] len Number of bytes at the location indicated by data 64 | * @param[out] result Resulting digest value 65 | */ 66 | void digest_get(const uint8_t *data, unsigned int len, 67 | uint8_t result[DIGEST_LEN]); 68 | 69 | /*! 70 | * @brief Converts a 32-bit value to a base 16 string 71 | * 72 | * @param[in] data Numeric value to convert 73 | * @param[out] result Resulting ASCII characters 74 | */ 75 | void digest_to_hex32(uint32_t data, char result[8]); 76 | 77 | /*! 78 | * @brief Converts the given digest to a null terminated string 79 | * 80 | * @param[in] md5 Input digest value 81 | * @param[out] result Resulting string representation of the given digest 82 | */ 83 | void digest_to_str(const uint8_t md5[DIGEST_LEN], 84 | char result[2 * DIGEST_LEN + 1]); 85 | 86 | /*! 87 | * @brief Converts a base 16 string to a 32-bit value 88 | * 89 | * @param[in] data ASCII characters to convert 90 | * @returns Resulting numeric value 91 | */ 92 | uint32_t hex32_to_digest(const char data[8]); 93 | 94 | #endif /* DIGEST_H_ */ 95 | -------------------------------------------------------------------------------- /test/test_digest.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file test_digest.c 3 | * 4 | * @copyright 5 | * Copyright © 2020, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Tests related to digest utilities 45 | */ 46 | 47 | #include 48 | #include 49 | #include 50 | 51 | #include "digest.h" 52 | 53 | /*! 54 | * @brief Test of the round-trip digest conversion routines 55 | * 56 | * @returns 0 on success, negative ERRNO value on failure 57 | * 58 | * @test Test of the round-trip digest conversion routines 59 | */ 60 | static int test_digest_conversion(void); 61 | 62 | /*! 63 | * @brief Main entry point for digest tests 64 | * 65 | * @returns 0 on success, non-zero value on failure 66 | */ 67 | int main(void); 68 | 69 | int main(void) 70 | { 71 | int ret = 0; 72 | 73 | ret |= test_digest_conversion(); 74 | 75 | return ret; 76 | } 77 | 78 | static int test_digest_conversion(void) 79 | { 80 | const uint32_t nonce = 0x4d3b6d47; 81 | static const char expected_response[9] = "4d3b6d47"; 82 | char response[9] = { 0x00 }; 83 | uint32_t round_trip; 84 | int ret = 0; 85 | 86 | digest_to_hex32(nonce, response); 87 | if (strcmp(expected_response, response) != 0) { 88 | fprintf(stderr, 89 | "Error: Conversion to hex32 failed. Expected: '%s'. Got: '%s'.\n", 90 | expected_response, response); 91 | ret |= -EINVAL; 92 | } 93 | 94 | round_trip = hex32_to_digest(expected_response); 95 | if (nonce != round_trip) { 96 | fprintf(stderr, 97 | "Error: Conversion from hex32 failed. Expected: 0x%08X. Got: 0x%08X.\n", 98 | nonce, round_trip); 99 | ret |= -EINVAL; 100 | } 101 | 102 | return ret; 103 | } 104 | -------------------------------------------------------------------------------- /test/test_proxy.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file test_proxy.c 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Tests related to the proxy itself 45 | */ 46 | 47 | #include 48 | #include 49 | 50 | #include "openelp/openelp.h" 51 | 52 | /*! 53 | * @brief Basic test of proxy password response generation 54 | * 55 | * @returns 0 on success, negative ERRNO value on failure 56 | * 57 | * @test Basic test of proxy password response generation 58 | */ 59 | static int test_proxy_password_response(void); 60 | 61 | /*! 62 | * @brief Main entry point for MD5 tests 63 | * 64 | * @returns 0 on success, non-zero value on failure 65 | */ 66 | int main(void); 67 | 68 | int main(void) 69 | { 70 | int ret = 0; 71 | 72 | ret |= test_proxy_password_response(); 73 | 74 | return ret; 75 | } 76 | 77 | static int test_proxy_password_response(void) 78 | { 79 | const uint32_t nonce = 0x4d3b6d47; 80 | static const char password[] = "asdf1234"; 81 | const uint8_t expected_response[16] = { 82 | 0x0c, 0x0b, 0xb9, 0x83, 0x5f, 0x31, 0x95, 0x53, 83 | 0x10, 0x4b, 0xf9, 0x10, 0xfb, 0x72, 0x45, 0xec, 84 | }; 85 | uint8_t response[16] = { 0x00 }; 86 | int ret; 87 | size_t i; 88 | 89 | ret = get_password_response(nonce, password, response); 90 | if (ret != 0) { 91 | fprintf(stderr, "Error: get_password_response returned %d\n", 92 | ret); 93 | return ret; 94 | } 95 | 96 | for (i = 0; i < 16; i++) { 97 | if (expected_response[i] != response[i]) { 98 | fprintf(stderr, 99 | "Error: Password response value missmatch at index %lu. Expected 0x%02hx, got 0x%02hx\n", 100 | (unsigned long)i, (uint16_t)expected_response[i], (uint16_t)response[i]); 101 | return -EINVAL; 102 | } 103 | } 104 | 105 | return ret; 106 | } 107 | -------------------------------------------------------------------------------- /test/test_md5.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file test_md5.c 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Tests related to MD5 generation 45 | */ 46 | 47 | #include 48 | #include 49 | #include 50 | 51 | #include "digest.h" 52 | 53 | /*! 54 | * @brief Basic test of MD5 generation 55 | * 56 | * @returns 0 on success, negative ERRNO value on failure 57 | * 58 | * @test Basic test of MD5 generation 59 | */ 60 | static int test_md5_basic(void); 61 | 62 | /*! 63 | * @brief Main entry point for MD5 tests 64 | * 65 | * @returns 0 on success, non-zero value on failure 66 | */ 67 | int main(void); 68 | 69 | int main(void) 70 | { 71 | int ret = 0; 72 | 73 | ret |= test_md5_basic(); 74 | 75 | return ret; 76 | } 77 | 78 | static int test_md5_basic(void) 79 | { 80 | static const char md5_challenge[] = "thequickbrownfox"; 81 | const uint8_t md5_control[16] = { 82 | 0x30, 0x8f, 0xb7, 0x6d, 0xc4, 0xd7, 0x30, 0x36, 83 | 0x0e, 0xe3, 0x39, 0x32, 0xd2, 0xfb, 0x10, 0x56, 84 | }; 85 | static const char md5_control_str[33] = "308FB76DC4D730360EE33932D2FB1056"; 86 | uint8_t md5_result[16] = { 0x00 }; 87 | char md5_result_str[33] = ""; 88 | 89 | digest_get((const uint8_t *)md5_challenge, 90 | (unsigned int)strlen(md5_challenge), md5_result); 91 | 92 | digest_to_str(md5_control, md5_result_str); 93 | 94 | if (strcmp(md5_control_str, md5_result_str) != 0) { 95 | fprintf(stderr, 96 | "Error: digest_to_str mismatch. Expected 0x%s Got: 0x%s\n", 97 | md5_control_str, md5_result_str); 98 | return -EINVAL; 99 | } 100 | 101 | digest_to_str(md5_result, md5_result_str); 102 | 103 | if (strcmp(md5_control_str, md5_result_str) != 0) { 104 | fprintf(stderr, 105 | "Error: digest_get mismatch. Expected: 0x%s Got: 0x%s\n", 106 | md5_control_str, md5_result_str); 107 | return -EINVAL; 108 | } 109 | 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /doc/ELProxy.conf: -------------------------------------------------------------------------------- 1 | # Sample EchoLink Proxy Configuration File 2 | 3 | # REQUIRED ITEMS 4 | 5 | # You must change the password to something besides "notset". 6 | Password=notset 7 | 8 | 9 | # OPTIONAL ITEMS 10 | 11 | # Change Port to something else if you don't want to use the default. 12 | # (If you use 1023 or below on Unix/Linux systems, you will need to run 13 | # EchoLink Proxy as root.) 14 | Port=8100 15 | 16 | 17 | # ADVANCED ITEMS 18 | 19 | # Set the ConnectionTimeout to something besides 0 if you want the proxy 20 | # to "pull the plug" on a client that has been using the proxy for more 21 | # than n minutes, regardless of whether an EchoLink connection is active. 22 | ConnectionTimeout=0 23 | 24 | # Set the BindAddress to something besides 0.0.0.0 if this is a computer 25 | # with multiple IP addresses and you want to associate this instance of 26 | # EchoLink Proxy with a single address only. 27 | BindAddress=0.0.0.0 28 | 29 | # Set the ExternalBindAddress to something besides 0.0.0.0 if this is a 30 | # multi-homed computer and you want to associate this instance of EchoLink 31 | # Proxy with a specific interface for its Internet communication. In this 32 | # situation, BindAddress will be used for the communication with the client, 33 | # and ExternalBindAddress will be used for the communication with the Internet. 34 | ExternalBindAddress=0.0.0.0 35 | 36 | # Set the PublicAddress if this is a multi-homed computer and the address 37 | # that appears on the Proxy Status Page is incorrect. In this situation, 38 | # the address you specify here will be the address that appears on the 39 | # Proxy Status page. This setting does not affect the behavior of the 40 | # Proxy itself, only the way it is listed. 41 | PublicAddress= 42 | 43 | # Set RegistrationName to something unique (such as your callsign) if you 44 | # want EchoLink Proxy to register itself with the EchoLink Web site. 45 | # Enable this option only if you want the IP address of this server to 46 | # be listed on the Web page. You might want to do this if the IP address 47 | # of your server changes from time to time and you need to find out what 48 | # it is. The Comment is optional. 49 | # Note: If you want to "advertise" this Proxy as a Public proxy, and make 50 | # it available to any EchoLink user, set the Password above to PUBLIC 51 | # and set RegistrationName below to something unique, such as your call. 52 | # This will cause the proxy to be also be listed on the Proxy List page 53 | # on the EchoLink Web site. Note that each RegistrationName must be 54 | # different for each instance. 55 | RegistrationName= 56 | RegistrationComment= 57 | 58 | # If you want to restrict access to the proxy only to certain callsigns, 59 | # create regular expressions for either or both of the following. A 60 | # connection will be denied if it matches the CallsignsDenied pattern, or 61 | # if it does NOT match the CallsignsAllowed pattern (and CallsignsAllowed 62 | # is not empty). These patterns apply only to EchoLink clients that use 63 | # this proxy, not the nodes to which they connect while using it. 64 | # Examples: CallsignsAllowed=K1RFD|AK8V will allow K1RFD and AK8V to use this proxy. 65 | # CallsignsDenied=.*-L$|.*-R$ will deny any Sysop nodes from using it. 66 | CallsignsDenied= 67 | CallsignsAllowed= 68 | 69 | # OPENELP ADVANCED ITEMS 70 | 71 | # Comma-separated list of additional addresses to bind to for use as external 72 | # interfaces. This is only possible if this is a multi-homed computer where 73 | # more than one of the interfaces access the internet using unique public- 74 | # facing addresses. These addresses should be unique, and should not be the 75 | # same as ExternalBindAddresses. If any addresses are specified here, none 76 | # of them can be 0.0.0.0 and ExternalBindAddress cannot be 0.0.0.0. 77 | AdditionalExternalBindAddresses= 78 | -------------------------------------------------------------------------------- /include/regex.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file regex.h 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Internal API for regular expression matching 45 | */ 46 | 47 | #ifndef REGEX_H_ 48 | #define REGEX_H_ 49 | 50 | /*! 51 | * @brief Represents an instance of a compiled regular expression 52 | * 53 | * This struct should be initialized to zero before being used. The private data 54 | * should be initialized using the ::regex_init function, and subsequently 55 | * freed by ::regex_free when the regular expression is no longer needed. 56 | */ 57 | struct regex_handle { 58 | /*! Private data - used internally by regex functions */ 59 | void *priv; 60 | }; 61 | 62 | /*! 63 | * @brief Compiles the given regular expression pattern 64 | * 65 | * @param[in,out] re Target regular expression instance 66 | * @param[in] pattern Regular expression pattern to be compiled 67 | * 68 | * @returns 0 on success, negative ERRNO value on failure 69 | */ 70 | int regex_compile(struct regex_handle *re, const char *pattern); 71 | 72 | /*! 73 | * @brief Frees data allocated by ::regex_init 74 | * 75 | * @param[in,out] re Target regular expression instance 76 | */ 77 | void regex_free(struct regex_handle *re); 78 | 79 | /*! 80 | * @brief Initializes the private data in a ::regex_handle 81 | * 82 | * @param[in,out] re Target regular expression instance 83 | * 84 | * @returns 0 on success, negative ERRNO value on failure 85 | */ 86 | int regex_init(struct regex_handle *re); 87 | 88 | /*! 89 | * @brief Tests the given subject against the regular expression 90 | * 91 | * @param[in] re Target regular expression instance 92 | * @param[in] subject Null terminated string to test against the regular 93 | * expression 94 | * 95 | * @returns 1 on match, 0 if no match, negative ERRNO value on failure 96 | */ 97 | int regex_is_match(const struct regex_handle *re, const char *subject); 98 | 99 | #endif /* REGEX_H_ */ 100 | -------------------------------------------------------------------------------- /include/thread.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file thread.h 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Internal API for threads 45 | */ 46 | 47 | #ifndef THREAD_H_ 48 | #define THREAD_H_ 49 | 50 | #include 51 | 52 | #include "mutex.h" 53 | 54 | /*! 55 | * @brief Represents an instance of a thread 56 | * 57 | * This struct should be initialized to zero before being used. The private data 58 | * should be initialized using the ::thread_init function, and subsequently 59 | * freed by ::thread_free when the thread is no longer needed. 60 | */ 61 | struct thread_handle { 62 | /*! Private data - used internally by thread functions */ 63 | void *priv; 64 | 65 | /*! Pointer to the function to be called by the thread when it starts */ 66 | void * (*func_ptr)(void *ctx); 67 | 68 | /*! Context to pass to thread_handle::func_ptr */ 69 | void *func_ctx; 70 | 71 | /*! Size for stack used for the thread */ 72 | unsigned int stack_size; 73 | }; 74 | 75 | /*! 76 | * @brief Frees data allocated by ::thread_init 77 | * 78 | * @param[in,out] th Target thread instance 79 | */ 80 | void thread_free(struct thread_handle *th); 81 | 82 | /*! 83 | * @brief Initializes the private data in a ::thread_handle 84 | * 85 | * @param[in,out] th Target thread instance 86 | * 87 | * @returns 0 on success, negative ERRNO value on failure 88 | */ 89 | int thread_init(struct thread_handle *th); 90 | 91 | /*! 92 | * @brief Blocks until the target thread returns 93 | * 94 | * @param[in,out] th Target thread instance 95 | * 96 | * @returns 0 on success, negative ERRNO value on failure 97 | */ 98 | int thread_join(struct thread_handle *th); 99 | 100 | /*! 101 | * @brief Starts the target thread instance 102 | * 103 | * @param[in,out] th Target thread instance 104 | * 105 | * @returns 0 on success, negative ERRNO value on failure 106 | */ 107 | int thread_start(struct thread_handle *th); 108 | 109 | #endif /* THREAD_H_ */ 110 | -------------------------------------------------------------------------------- /src/pearson.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file pearson.c 3 | * 4 | * @copyright 5 | * Copyright © 2020, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Implmentation of a Pearson hashing function 45 | */ 46 | 47 | #include "pearson.h" 48 | 49 | /*! 50 | * @brief Hash computation permutation table 51 | * 52 | * Chosen by fair dice roll. 53 | * Guaranteed to be random. 54 | */ 55 | static const uint8_t permutation_table[256] = { 56 | 5, 42, 6, 37, 103, 84, 75, 83, 57 | 219, 54, 116, 223, 192, 239, 163, 154, 58 | 114, 189, 206, 105, 92, 76, 162, 225, 59 | 158, 125, 74, 234, 160, 29, 153, 198, 60 | 132, 34, 93, 86, 161, 159, 181, 66, 61 | 197, 82, 204, 59, 237, 134, 36, 57, 62 | 95, 39, 90, 32, 150, 217, 30, 202, 63 | 174, 89, 38, 216, 117, 52, 169, 232, 64 | 43, 254, 126, 222, 170, 129, 49, 207, 65 | 73, 138, 120, 25, 27, 190, 100, 229, 66 | 172, 67, 199, 210, 214, 218, 62, 18, 67 | 97, 63, 101, 7, 20, 187, 131, 179, 68 | 188, 247, 135, 68, 195, 71, 85, 144, 69 | 107, 241, 182, 231, 98, 60, 245, 91, 70 | 119, 87, 12, 51, 146, 96, 47, 45, 71 | 22, 40, 1, 205, 88, 215, 141, 108, 72 | 139, 23, 133, 78, 61, 21, 72, 226, 73 | 183, 53, 106, 173, 224, 31, 44, 80, 74 | 104, 167, 145, 81, 46, 10, 244, 193, 75 | 246, 55, 112, 19, 58, 176, 221, 255, 76 | 209, 196, 147, 14, 127, 79, 111, 157, 77 | 249, 26, 177, 168, 212, 69, 13, 115, 78 | 238, 227, 180, 33, 230, 109, 228, 121, 79 | 142, 156, 28, 64, 110, 118, 165, 184, 80 | 3, 149, 128, 203, 220, 186, 50, 213, 81 | 171, 201, 164, 242, 191, 11, 152, 178, 82 | 252, 194, 123, 48, 2, 130, 24, 124, 83 | 113, 248, 251, 137, 253, 208, 99, 136, 84 | 166, 0, 65, 4, 185, 15, 250, 235, 85 | 16, 9, 200, 211, 35, 175, 70, 155, 86 | 243, 240, 143, 122, 236, 233, 8, 148, 87 | 102, 56, 77, 41, 94, 140, 17, 151, 88 | }; 89 | 90 | uint8_t pearson_get(const uint8_t *data, size_t data_len) 91 | { 92 | uint8_t ret = data_len % 256; 93 | 94 | if (data_len == 0) 95 | return 0; 96 | 97 | do { 98 | ret = permutation_table[ret ^ *data++]; 99 | } while (--data_len > 0); 100 | 101 | return ret; 102 | } 103 | -------------------------------------------------------------------------------- /doc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Dependencies 3 | # 4 | 5 | find_package(Doxygen) 6 | 7 | # 8 | # Targets 9 | # 10 | 11 | if(DOXYGEN_FOUND) 12 | set(OPENELP_DOC_README "\"${OPENELP_DIR}/README.md\"") 13 | set(OPENELP_DOC_SOURCES "\"${OPENELP_INCLUDE_DIR}/openelp/openelp.h\" \\ 14 | \"${OPENELP_DIR}/CONTRIBUTING.md\" \\ 15 | \"${OPENELP_DIR}/TODO.md\" \\ 16 | ${OPENELP_DOC_README}") 17 | 18 | if(OPENELP_DOC_INTERNAL) 19 | set(OPENELP_DOC_SOURCES "${OPENELP_DOC_SOURCES} \\ 20 | \"${OPENELP_INCLUDE_DIR}\" \\ 21 | \"${OPENELP_SOURCE_DIR}\" \\ 22 | \"${OPENELP_TEST_DIR}\"") 23 | set(OPENELP_DOC_INTERNAL_YN "YES") 24 | else() 25 | set(OPENELP_DOC_INTERNAL_YN "NO") 26 | endif() 27 | 28 | if(OPENELP_DOC_HTMLHELP) 29 | set(OPENELP_DOC_HTMLHELP_YN "YES") 30 | set(OPENELP_DOC_SEARCH_YN "NO") 31 | set(OPENELP_DOC_HTMLHELP_OUT_PATH 32 | "${CMAKE_CURRENT_BINARY_DIR}/html/openelp.chm" 33 | ) 34 | set(OPENELP_DOC_HTMLHELP_CMD 35 | "${CMAKE_CURRENT_BINARY_DIR}/hhc.cmd" 36 | ) 37 | 38 | string(REPLACE "/" "\\" 39 | OPENELP_DOC_HTMLHELP_OUT_PATH_BS 40 | "${OPENELP_DOC_HTMLHELP_OUT_PATH}" 41 | ) 42 | string(REPLACE "/" "\\" 43 | OPENELP_DOC_HTMLHELP_PATH_BS 44 | "${OPENELP_DOC_HTMLHELP_PATH}" 45 | ) 46 | string(REPLACE "/" "\\" 47 | OPENELP_DOC_HTMLHELP_CMD_BS 48 | "${OPENELP_DOC_HTMLHELP_CMD}" 49 | ) 50 | 51 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/hhc.cmd.in" 52 | "${OPENELP_DOC_HTMLHELP_CMD}" 53 | @ONLY) 54 | 55 | set(OPENELP_DOC_ALL "ALL") 56 | else() 57 | set(OPENELP_DOC_HTMLHELP_YN "NO") 58 | set(OPENELP_DOC_SEARCH_YN "YES") 59 | set(OPENELP_DOC_HTMLHELP_OUT_PATH "") 60 | set(OPENELP_DOC_ALL "") 61 | endif() 62 | 63 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in" 64 | "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile" 65 | @ONLY) 66 | 67 | add_custom_command(OUTPUT 68 | ${OPENELP_DOC_HTMLHELP_OUT_PATH} 69 | "${CMAKE_CURRENT_BINARY_DIR}/html/index.html" 70 | COMMAND "${DOXYGEN_EXECUTABLE}" 71 | ARGS "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile" 72 | MAIN_DEPENDENCY "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile" 73 | DEPENDS 74 | "${OPENELP_DOC_HTMLHELP_CMD}" 75 | "${OPENELP_INCLUDE_DIR}/openelp/openelp.h" 76 | "${OPENELP_DIR}/CONTRIBUTING.md" 77 | "${OPENELP_DIR}/TODO.md" 78 | "${OPENELP_DIR}/README.md" 79 | WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" 80 | COMMENT "Generating API documentation with Doxygen" VERBATIM 81 | ) 82 | 83 | 84 | add_custom_target(doc ${OPENELP_DOC_ALL} 85 | DEPENDS 86 | ${OPENELP_DOC_HTMLHELP_OUT_PATH} 87 | "${CMAKE_CURRENT_BINARY_DIR}/html/index.html" 88 | ) 89 | else() 90 | message("Could not find Doxygen - not generating API documentation") 91 | endif() 92 | 93 | # 94 | # Install 95 | # 96 | 97 | install(FILES "ELProxy.conf" 98 | DESTINATION "${SYSCONF_INSTALL_DIR}" 99 | COMPONENT config 100 | ) 101 | 102 | if(UNIX) 103 | install(FILES openelpd.1 104 | DESTINATION "${MAN_INSTALL_DIR}/man1" 105 | COMPONENT docs 106 | ) 107 | 108 | if(NOT APPLE) 109 | get_filename_component(FULL_BIN_INSTALL_DIR 110 | ${BIN_INSTALL_DIR} REALPATH BASE_DIR ${CMAKE_INSTALL_PREFIX}) 111 | get_filename_component(FULL_SYSCONF_INSTALL_DIR 112 | ${SYSCONF_INSTALL_DIR} REALPATH BASE_DIR ${CMAKE_INSTALL_PREFIX}) 113 | 114 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/openelp.service.in" 115 | "${CMAKE_CURRENT_BINARY_DIR}/openelp.service" 116 | @ONLY) 117 | 118 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/openelp.service" 119 | DESTINATION "${SYSTEMD_SERVICES_INSTALL_DIR}" 120 | COMPONENT systemd 121 | ) 122 | endif() 123 | endif() 124 | 125 | if(OPENELP_DOC_HTMLHELP AND DOXYGEN_FOUND) 126 | install(FILES "${OPENELP_DOC_HTMLHELP_OUT_PATH}" 127 | DESTINATION "." 128 | COMPONENT help 129 | ) 130 | endif() 131 | 132 | add_subdirectory("${OPENELP_DOC_DIR}/icons") 133 | -------------------------------------------------------------------------------- /src/regex.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file regex.c 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Implementation of the regular expression matcher 45 | */ 46 | 47 | #include 48 | #include 49 | #include 50 | 51 | #include 52 | 53 | #include "regex.h" 54 | 55 | /*! 56 | * @brief Private data for an instance of a compiled regular expression 57 | */ 58 | struct regex_priv { 59 | /*! Perl Compatible Regular Expression */ 60 | pcre2_code *re; 61 | }; 62 | 63 | int regex_compile(struct regex_handle *re, const char *pattern) 64 | { 65 | struct regex_priv *priv = re->priv; 66 | int errorcode; 67 | PCRE2_SIZE erroroffset; 68 | int ret; 69 | 70 | if (priv->re != NULL) 71 | pcre2_code_free(priv->re); 72 | 73 | priv->re = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, 0, 74 | &errorcode, &erroroffset, NULL); 75 | if (priv->re == NULL) { 76 | ret = -EINVAL; 77 | goto regex_compile_exit; 78 | } 79 | 80 | return 0; 81 | 82 | regex_compile_exit: 83 | if (priv->re != NULL) { 84 | pcre2_code_free(priv->re); 85 | priv->re = NULL; 86 | } 87 | 88 | return ret; 89 | } 90 | 91 | void regex_free(struct regex_handle *re) 92 | { 93 | if (re->priv != NULL) { 94 | struct regex_priv *priv = re->priv; 95 | 96 | if (priv->re != NULL) 97 | pcre2_code_free(priv->re); 98 | 99 | free(re->priv); 100 | re->priv = NULL; 101 | } 102 | } 103 | 104 | int regex_init(struct regex_handle *re) 105 | { 106 | struct regex_priv *priv = re->priv; 107 | 108 | if (priv == NULL) { 109 | priv = calloc(1, sizeof(*priv)); 110 | if (priv == NULL) 111 | return -ENOMEM; 112 | 113 | re->priv = priv; 114 | } 115 | 116 | return 0; 117 | } 118 | 119 | int regex_is_match(const struct regex_handle *re, const char *subject) 120 | { 121 | const struct regex_priv *priv = (const struct regex_priv *)re->priv; 122 | PCRE2_SPTR sub = (PCRE2_SPTR)subject; 123 | size_t sub_len = strlen(subject); 124 | pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(priv->re, NULL); 125 | int ret; 126 | 127 | if (match_data == NULL) 128 | return -EINVAL; 129 | 130 | ret = pcre2_match(priv->re, sub, sub_len, 0, 0, match_data, NULL); 131 | 132 | pcre2_match_data_free(match_data); 133 | 134 | if (ret < 0) { 135 | if (ret == PCRE2_ERROR_NOMATCH) 136 | return 0; 137 | else 138 | return -EINVAL; 139 | } else { 140 | return 1; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /include/registration.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file registration.h 3 | * 4 | * @copyright 5 | * Copyright © 2020, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Internal API for proxy server registration 45 | */ 46 | 47 | #ifndef REGISTRATION_H_ 48 | #define REGISTRATION_H_ 49 | 50 | #include "conf.h" 51 | 52 | /*! 53 | * @brief Represents an instance of proxy registration service 54 | * 55 | * This struct should be initialized to zero before being used. The private data 56 | * should be initialized using the ::registration_service_init function, and 57 | * subsequently freed by ::registration_service_free when the registration 58 | * infrastructure is no longer needed. 59 | */ 60 | struct registration_service_handle { 61 | /*! Private data - used internally by registration_service functions */ 62 | void *priv; 63 | }; 64 | 65 | /*! 66 | * @brief Frees data allocated by ::registration_service_init 67 | * 68 | * @param[in,out] rs Target registration service instance 69 | */ 70 | void registration_service_free(struct registration_service_handle *rs); 71 | 72 | /*! 73 | * @brief Initializes the private data in a ::registration_service_handle 74 | * 75 | * @param[in,out] rs Target registration service instance 76 | * 77 | * @returns 0 on success, negative ERRNO value on failure 78 | */ 79 | int registration_service_init(struct registration_service_handle *rs); 80 | 81 | /*! 82 | * @brief Starts the registration service thread 83 | * 84 | * @param[in,out] rs Target registration service instance 85 | * @param[in] conf Proxy configuration 86 | * 87 | * @returns 0 on success, negative ERRNO value on failure 88 | */ 89 | int registration_service_start(struct registration_service_handle *rs, 90 | const struct proxy_conf *conf); 91 | 92 | /*! 93 | * @brief Sends a final status message and stops the registration thread 94 | * 95 | * @param[in,out] rs Target registration service instance 96 | * 97 | * @returns 0 on success, negative ERRNO value on failure 98 | */ 99 | int registration_service_stop(struct registration_service_handle *rs); 100 | 101 | /*! 102 | * @brief Queues a registration status message update 103 | * 104 | * @param[in,out] rs Target registration service instance 105 | * @param[in] slots_used Number of proxy slots currently in use 106 | * @param[in] slots_total Total number of configured proxy slots 107 | */ 108 | void registration_service_update(struct registration_service_handle *rs, 109 | size_t slots_used, size_t slots_total); 110 | 111 | #endif /* REGISTRATION_H_ */ 112 | -------------------------------------------------------------------------------- /include/proxy_msg.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file proxy_msg.h 3 | * 4 | * @copyright 5 | * Copyright © 2025, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Proxy message format 45 | */ 46 | 47 | #ifndef PROXY_MSG_H_ 48 | #define PROXY_MSG_H_ 49 | 50 | #include 51 | 52 | /*! 53 | * @brief Message types used in communication between the proxy and the client 54 | */ 55 | enum PROXY_MSG_TYPE { 56 | /*! 57 | * @brief The proxy should open a new TCP connection 58 | * 59 | * * Sent by: client 60 | * * Expected data: 0 bytes 61 | */ 62 | PROXY_MSG_TYPE_TCP_OPEN = 1, 63 | 64 | /*! 65 | * @brief Data which has been sent or should be sent over the TCP connection 66 | * 67 | * The address field is ignored in this message 68 | * 69 | * * Sent by: client or proxy 70 | * * Expected data: 1 or more bytes 71 | */ 72 | PROXY_MSG_TYPE_TCP_DATA, 73 | 74 | /*! 75 | * @brief The TCP has been, or should be closed 76 | * 77 | * The address field is ignored in this message 78 | * 79 | * When the client requests that the TCP connection be closed, the proxy 80 | * responds with another ::PROXY_MSG_TYPE_TCP_CLOSE message 81 | * 82 | * * Sent by: client or proxy 83 | * * Expected data: 0 bytes 84 | */ 85 | PROXY_MSG_TYPE_TCP_CLOSE, 86 | 87 | /*! 88 | * @brief The status of the TCP connection 89 | * 90 | * The address field is ignored in this message 91 | * 92 | * The data included with this message should be zeroed when the TCP connection 93 | * was opened successfully, and non-zero otherwise 94 | * 95 | * * Sent by: proxy 96 | * * Expected data: 4 bytes 97 | */ 98 | PROXY_MSG_TYPE_TCP_STATUS, 99 | 100 | /*! 101 | * @brief Data which has been or should be sent of the UDP Data connection 102 | * 103 | * * Sent by: client or proxy 104 | * * Expected data: 1 or more bytes 105 | */ 106 | PROXY_MSG_TYPE_UDP_DATA, 107 | 108 | /*! 109 | * @brief Data which has been or should be sent of the UDP Control connection 110 | * 111 | * * Sent by: client or proxy 112 | * * Expected data: 1 or more bytes 113 | */ 114 | PROXY_MSG_TYPE_UDP_CONTROL 115 | }; 116 | 117 | #ifdef _WIN32 118 | # pragma pack(push, 1) 119 | #endif 120 | /*! 121 | * @brief Proxy message header 122 | */ 123 | struct proxy_msg { 124 | /*! Type of proxy message, should be one of ::PROXY_MSG_TYPE */ 125 | uint8_t type; 126 | 127 | /*! 32-bit IPv4 address, if applicable */ 128 | uint32_t address; 129 | 130 | /*! Number of bytes in proxy_msg::data */ 131 | uint32_t size; 132 | #ifdef _WIN32 133 | }; 134 | # pragma pack(pop) 135 | #else 136 | } __attribute__((packed)); 137 | #endif 138 | 139 | #endif /* PROXY_MSG_H_ */ 140 | -------------------------------------------------------------------------------- /src/thread_posix.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file thread_posix.c 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Threading implementation for POSIX machines 45 | */ 46 | 47 | #include 48 | #include 49 | #include 50 | 51 | #include 52 | 53 | #include "mutex.h" 54 | #include "thread.h" 55 | 56 | /*! 57 | * @brief Private data for an instance of a POSIX thread 58 | */ 59 | struct thread_priv { 60 | /*! POSIX thread */ 61 | pthread_t thread; 62 | 63 | /*! Mutex for protecting the thread handle */ 64 | struct mutex_handle mutex; 65 | 66 | /*! Boolean value indicating if the mutex has been initialized */ 67 | uint8_t dirty; 68 | }; 69 | 70 | void thread_free(struct thread_handle *pt) 71 | { 72 | struct thread_priv *priv = pt->priv; 73 | 74 | if (pt->priv != NULL) { 75 | thread_join(pt); 76 | 77 | mutex_free(&priv->mutex); 78 | 79 | free(pt->priv); 80 | pt->priv = NULL; 81 | } 82 | } 83 | 84 | int thread_init(struct thread_handle *pt) 85 | { 86 | struct thread_priv *priv = pt->priv; 87 | int ret; 88 | 89 | if (priv == NULL) { 90 | priv = calloc(1, sizeof(*priv)); 91 | if (priv == NULL) 92 | return -ENOMEM; 93 | 94 | pt->priv = priv; 95 | } 96 | 97 | ret = mutex_init(&priv->mutex); 98 | if (ret < 0) 99 | goto thread_init_exit; 100 | 101 | return 0; 102 | 103 | thread_init_exit: 104 | free(pt->priv); 105 | pt->priv = NULL; 106 | 107 | return ret; 108 | } 109 | 110 | int thread_join(struct thread_handle *pt) 111 | { 112 | struct thread_priv *priv = pt->priv; 113 | int ret; 114 | 115 | mutex_lock(&priv->mutex); 116 | 117 | if (!priv->dirty) { 118 | ret = 0; 119 | } else { 120 | ret = pthread_join(priv->thread, NULL); 121 | 122 | priv->dirty = 0; 123 | 124 | if (ret == 0) 125 | memset(&priv->thread, 0x0, sizeof(priv->thread)); 126 | } 127 | 128 | mutex_unlock(&priv->mutex); 129 | 130 | return ret > 0 ? -ret : ret; 131 | } 132 | 133 | int thread_start(struct thread_handle *pt) 134 | { 135 | struct thread_priv *priv = pt->priv; 136 | pthread_attr_t attr; 137 | int ret; 138 | 139 | ret = pthread_attr_init(&attr); 140 | if (ret != 0) 141 | return ret > 0 ? -ret : ret; 142 | 143 | if (pt->stack_size > 0) { 144 | ret = pthread_attr_setstacksize(&attr, pt->stack_size); 145 | if (ret != 0) 146 | return ret > 0 ? -ret : ret; 147 | } 148 | 149 | thread_join(pt); 150 | 151 | mutex_lock(&priv->mutex); 152 | 153 | ret = pthread_create(&priv->thread, &attr, pt->func_ptr, pt); 154 | 155 | priv->dirty = !(ret); 156 | 157 | mutex_unlock(&priv->mutex); 158 | 159 | return ret > 0 ? -ret : ret; 160 | } 161 | -------------------------------------------------------------------------------- /src/thread_win.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file thread_win.c 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Threading implementation for Windows 45 | */ 46 | 47 | #include 48 | #include 49 | #include 50 | 51 | #include 52 | 53 | #include "mutex.h" 54 | #include "thread.h" 55 | 56 | /*! 57 | * @brief Private data for an instance of a Windows thread 58 | */ 59 | struct thread_priv { 60 | /*! Handle to the thread */ 61 | HANDLE thread; 62 | 63 | /*! Mutex for protecting the thread handle */ 64 | struct mutex_handle mutex; 65 | }; 66 | 67 | /*! 68 | * @brief Wrapper for Windows worker function calling syntax 69 | * 70 | * @param ctx Worker function context 71 | * 72 | * @returns Exit code of the thread 73 | * 74 | * Since the Windows API for threading uses a different worker function 75 | * signature than the one used by these threading routines, this wrapper 76 | * function is used to call the worker function specified in the thread 77 | * handle. 78 | */ 79 | static DWORD WINAPI windows_thread_wrapper(LPVOID ctx) 80 | { 81 | struct thread_handle *pt = ctx; 82 | 83 | pt->func_ptr(pt); 84 | 85 | return 0; 86 | } 87 | 88 | void thread_free(struct thread_handle *pt) 89 | { 90 | struct thread_priv *priv = pt->priv; 91 | 92 | if (pt->priv != NULL) { 93 | thread_join(pt); 94 | 95 | mutex_free(&priv->mutex); 96 | 97 | free(pt->priv); 98 | pt->priv = NULL; 99 | } 100 | } 101 | 102 | int thread_init(struct thread_handle *pt) 103 | { 104 | struct thread_priv *priv = pt->priv; 105 | int ret; 106 | 107 | if (priv == NULL) { 108 | priv = calloc(1, sizeof(*priv)); 109 | if (priv == NULL) 110 | return -ENOMEM; 111 | 112 | pt->priv = priv; 113 | } 114 | 115 | ret = mutex_init(&priv->mutex); 116 | if (ret < 0) 117 | goto thread_init_exit; 118 | 119 | return 0; 120 | 121 | thread_init_exit: 122 | free(pt->priv); 123 | pt->priv = NULL; 124 | 125 | return ret; 126 | } 127 | 128 | int thread_join(struct thread_handle *pt) 129 | { 130 | struct thread_priv *priv = pt->priv; 131 | int ret; 132 | 133 | mutex_lock(&priv->mutex); 134 | 135 | ret = WaitForSingleObject(priv->thread, INFINITE); 136 | 137 | CloseHandle(priv->thread); 138 | 139 | priv->thread = NULL; 140 | 141 | mutex_unlock(&priv->mutex); 142 | 143 | if (ret != WAIT_OBJECT_0) 144 | ret = GetLastError(); 145 | 146 | return ret; 147 | } 148 | 149 | int thread_start(struct thread_handle *pt) 150 | { 151 | struct thread_priv *priv = pt->priv; 152 | 153 | mutex_lock(&priv->mutex); 154 | 155 | priv->thread = CreateThread(NULL, 0, windows_thread_wrapper, pt, 0, 156 | NULL); 157 | 158 | mutex_unlock(&priv->mutex); 159 | 160 | return priv->thread == NULL ? -ECHILD : 0; 161 | } 162 | -------------------------------------------------------------------------------- /include/proxy_client.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file proxy_client.h 3 | * 4 | * @copyright 5 | * Copyright © 2020, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Internal API for a client connection 45 | */ 46 | 47 | #ifndef PROXY_CLIENT_H_ 48 | #define PROXY_CLIENT_H_ 49 | 50 | #include 51 | 52 | #include "proxy_msg.h" 53 | 54 | /*! 55 | * @brief Represents an instance of a client connection 56 | * 57 | * This struct should be initialized to zero before being used. The private 58 | * data should be initialized using the ::proxy_client_init function, and 59 | * subsequently freed by ::proxy_client_free when the worker is no longer 60 | * needed. 61 | */ 62 | struct proxy_client_handle { 63 | /*! Private data - used internally by proxy_client functions */ 64 | void *priv; 65 | 66 | /*! Hostname or address of the proxy server */ 67 | char *host_addr; 68 | 69 | /*! Port number of the proxy server */ 70 | char *host_port; 71 | 72 | /*! The callsign to use during proxy authorization */ 73 | char *callsign; 74 | 75 | /*! The password to use during proxy authorization */ 76 | char *password; 77 | }; 78 | 79 | /*! 80 | * @brief Connect to the proxy server 81 | * 82 | * @param[in,out] ch Target client connection instance 83 | * 84 | * @returns 0 on success, negative ERRNO value on failure 85 | */ 86 | int proxy_client_connect(struct proxy_client_handle *ch); 87 | 88 | /*! 89 | * @brief Disconnect from the proxy server 90 | * 91 | * @param[in,out] ch Target client connection instance 92 | */ 93 | void proxy_client_disconnect(struct proxy_client_handle *ch); 94 | 95 | /*! 96 | * @brief Frees data allocated by ::proxy_client_init 97 | * 98 | * @param[in,out] ch Target client connection instance 99 | */ 100 | void proxy_client_free(struct proxy_client_handle *ch); 101 | 102 | /*! 103 | * @brief Initializes the private data in a ::proxy_client_handle 104 | * 105 | * @param[in,out] ch Target client connection instance 106 | * 107 | * @returns 0 on success, negative ERRNO value on failure 108 | */ 109 | int proxy_client_init(struct proxy_client_handle *ch); 110 | 111 | /*! 112 | * @brief Copies a message which has been sent from the proxy server 113 | * 114 | * @param[in] ch Target client connection instance 115 | * @param[out] msg Received message header 116 | * @param[out] buff Buffer to copy received into 117 | * @param[in] buff_len Maximum number of bytes of data to read 118 | * 119 | * @returns 0 on success, negative ERRNO value on failure 120 | */ 121 | int proxy_client_recv(struct proxy_client_handle *ch, struct proxy_msg *msg, 122 | uint8_t *buff, size_t buff_len); 123 | 124 | /*! 125 | * @brief Send a message to the connected proxy server 126 | * 127 | * @param[in] ch Target client connection instance 128 | * @param[in] msg Message header to send 129 | * @param[in] buff Buffer containing data to be sent 130 | * 131 | * @returns 0 on success, negative ERRNO value on failure 132 | */ 133 | int proxy_client_send(struct proxy_client_handle *ch, 134 | const struct proxy_msg *msg, const uint8_t *buff); 135 | 136 | #endif /* PROXY_CLIENT_H_ */ 137 | -------------------------------------------------------------------------------- /cmake/Package.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # CPack Configuration 3 | # 4 | 5 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Open Source EchoLink Proxy") 6 | 7 | set(CPACK_PACKAGE_EXECUTABLES "openelpd;openelpd") 8 | 9 | set(CPACK_PACKAGE_INSTALL_DIRECTORY "OpenELP") 10 | set(CPACK_PACKAGE_NAME "OpenELP") 11 | set(CPACK_PACKAGE_VENDOR "Scott K Logan (KM0H)") 12 | set(CPACK_PACKAGE_CONTACT "logans@cottsay.net") 13 | 14 | set(CPACK_PACKAGE_VERSION_MAJOR "${OPENELP_MAJOR_VERSION}") 15 | set(CPACK_PACKAGE_VERSION_MINOR "${OPENELP_MINOR_VERSION}") 16 | set(CPACK_PACKAGE_VERSION_PATCH "${OPENELP_PATCH_VERSION}") 17 | set(CPACK_PACKAGE_VERSION "${OPENELP_VERSION}") 18 | 19 | set(CPACK_RESOURCE_FILE_LICENSE "${OPENELP_DIR}/LICENSE") 20 | 21 | # There is an NSIS bug here - we have to use a backslash 22 | set(CPACK_PACKAGE_ICON "${OPENELP_DOC_DIR}/icons\\\\installer.bmp") 23 | 24 | set(CPACK_NSIS_INSTALLED_ICON_NAME "bin/openelpd.exe") 25 | 26 | set(CPACK_COMPONENTS_ALL 27 | app 28 | config 29 | devel 30 | libs 31 | license 32 | ) 33 | 34 | if(OPENELP_DOC_HTMLHELP) 35 | list(APPEND CPACK_COMPONENTS_ALL "help") 36 | set(CPACK_NSIS_CREATE_ICONS_EXTRA "${CPACK_NSIS_CREATE_ICONS_EXTRA} 37 | CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\OpenELP Help.lnk' '$INSTDIR\\\\openelp.chm' ''" 38 | ) 39 | set(CPACK_NSIS_DELETE_ICONS_EXTRA "${CPACK_NSIS_DELETE_ICONS_EXTRA} 40 | Delete '$SMPROGRAMS\\\\$START_MENU\\\\OpenELP Help.lnk'" 41 | ) 42 | endif() 43 | 44 | if(WIN32) 45 | list(APPEND CPACK_COMPONENTS_ALL "runtime" "service") 46 | endif() 47 | 48 | set(CPACK_COMPONENT_LIBS_REQUIRED TRUE) 49 | set(CPACK_COMPONENT_LICENSE_HIDDEN TRUE) 50 | set(CPACK_COMPONENT_HELP_HIDDEN TRUE) 51 | set(CPACK_COMPONENT_APP_DISPLAY_NAME "Executable Application") 52 | set(CPACK_COMPONENT_CONFIG_DISPLAY_NAME "Sample Configuration") 53 | set(CPACK_COMPONENT_DEVEL_DISPLAY_NAME "Development Files") 54 | set(CPACK_COMPONENT_LIBS_DISPLAY_NAME "Proxy Library") 55 | set(CPACK_COMPONENT_RUNTIME_DISPLAY_NAME "System Runtime Libraries") 56 | set(CPACK_COMPONENT_SERVICE_DISPLAY_NAME "Windows Service") 57 | set(CPACK_COMPONENT_APP_DESCRIPTION 58 | "Simple executable application which opens an EchoLink proxy" 59 | ) 60 | set(CPACK_COMPONENT_CONFIG_DESCRIPTION 61 | "Sample configuration file for customizing an EchoLink proxy" 62 | ) 63 | set(CPACK_COMPONENT_DEVEL_DESCRIPTION 64 | "C headers and library files used by other projects to build using OpenELP" 65 | ) 66 | set(CPACK_COMPONENT_LIBS_DESCRIPTION 67 | "Library used by applications to set up and control an EchoLink proxy" 68 | ) 69 | set(CPACK_COMPONENT_RUNTIME_DESCRIPTION 70 | "System runtime libraries, needed for OpenELP but not always installed on hosts" 71 | ) 72 | set(CPACK_COMPONENT_SERVICE_DESCRIPTION 73 | "Windows background service" 74 | ) 75 | set(CPACK_COMPONENT_APP_DEPENDS libs) 76 | set(CPACK_COMPONENT_DEVEL_DEPENDS libs) 77 | set(CPACK_COMPONENT_SERVICE_DEPENDS libs) 78 | set(CPACK_COMPONENT_DEVEL_DISABLED True) 79 | 80 | set(CPACK_NSIS_URL_INFO_ABOUT "http://github.com/cottsay/openelp") 81 | set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS} 82 | nsExec::Exec '$INSTDIR\\\\bin\\\\openelp_service.exe install' 83 | !include 'FileFunc.nsh' 84 | \\\${GetSize} '$INSTDIR' '/S=0K' $0 $1 $2 85 | IntFmt $0 '0x%08X' $0 86 | WriteRegDWORD HKLM 'SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall\\\\OpenELP' 'EstimatedSize' '$0' 87 | WriteRegStr HKLM 'SYSTEM\\\\CurrentControlSet\\\\Services\\\\EventLog\\\\Application\\\\OpenELP' 'EventMessageFile' '$INSTDIR\\\\bin\\\\openelp.dll' 88 | WriteRegDWORD HKLM 'SYSTEM\\\\CurrentControlSet\\\\Services\\\\EventLog\\\\Application\\\\OpenELP' 'TypesSupported' '0x07' 89 | SectionGetFlags \\\${config} $0 90 | IntOp $0 $0 & \\\${SF_SELECTED} 91 | IntCmp $0 0 no_config 92 | CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\ELProxy.conf.lnk' 'notepad.exe' '$INSTDIR\\\\ELProxy.conf' 93 | no_config:" 94 | ) 95 | set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "${CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS} 96 | nsExec::Exec '$INSTDIR\\\\bin\\\\openelp_service.exe uninstall' 97 | DeleteRegKey HKLM 'SYSTEM\\\\CurrentControlSet\\\\Services\\\\EventLog\\\\Application\\\\OpenELP' 98 | !insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP 99 | Delete '$SMPROGRAMS\\\\$MUI_TEMP\\\\ELProxy.conf.lnk'" 100 | ) 101 | 102 | include(CPack) 103 | 104 | # 105 | # Licenses 106 | # 107 | 108 | if(WIN32) 109 | install(FILES "${OPENELP_DIR}/LICENSE" 110 | DESTINATION "." 111 | COMPONENT license 112 | RENAME "LICENSE.txt" 113 | ) 114 | 115 | if(OPENELP_BUNDLE_PCRE) 116 | add_custom_command(TARGET pcre POST_BUILD 117 | COMMAND ${CMAKE_COMMAND} -P ${OPENELP_DIR}/cmake/unix2dos.cmake 118 | "${OPENELP_PCRE_LICENSE_PATH}" 119 | "${CMAKE_CURRENT_BINARY_DIR}/LICENSE.pcre" 120 | ) 121 | 122 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/LICENSE.pcre" 123 | DESTINATION "." 124 | COMPONENT license 125 | RENAME "LICENSE.pcre.txt" 126 | ) 127 | endif() 128 | endif() 129 | -------------------------------------------------------------------------------- /include/worker.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file worker.h 3 | * 4 | * @copyright 5 | * Copyright © 2020, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Internal API for threaded workers 45 | */ 46 | 47 | #ifndef WORKER_H_ 48 | #define WORKER_H_ 49 | 50 | #include 51 | 52 | #include "thread.h" 53 | 54 | /*! 55 | * @brief Represents an instance of a worker 56 | * 57 | * This struct should be initialized to zero before being used. The private data 58 | * should be initialized using the ::worker_init function, and subsequently 59 | * freed by ::worker_free when the worker is no longer needed. 60 | */ 61 | struct worker_handle { 62 | /*! Private data - used internally by worker functions */ 63 | void *priv; 64 | 65 | /*! Pointer to the function called when work is available */ 66 | void (*func_ptr)(struct worker_handle *wh); 67 | 68 | /*! Context to pass to worker_handle::func_ptr */ 69 | void *func_ctx; 70 | 71 | /*! Size for stack used for the thread */ 72 | unsigned int stack_size; 73 | 74 | /*! Optional maximum idle time in milliseconds between work */ 75 | uint32_t periodic_wake; 76 | }; 77 | 78 | /*! 79 | * @brief Frees data allocated by ::worker_init 80 | * 81 | * @param[in,out] wh Target worker instance 82 | */ 83 | void worker_free(struct worker_handle *wh); 84 | 85 | /*! 86 | * @brief Initializes the private data in a ::worker_handle 87 | * 88 | * @param[in,out] wh Target worker instance 89 | * 90 | * @returns 0 on success, negative ERRNO value on failure 91 | */ 92 | int worker_init(struct worker_handle *wh); 93 | 94 | /*! 95 | * @brief Determine if the worker is currently waiting for work 96 | * 97 | * @param[in,out] wh Target worker instance 98 | * 99 | * @returns 1 if idle, 0 otherwise 100 | */ 101 | int worker_is_idle(struct worker_handle *wh); 102 | 103 | /*! 104 | * @brief Blocks until the target worker stops 105 | * 106 | * @param[in,out] wh Target worker instance 107 | * 108 | * @returns 0 on success, negative ERRNO value on failure 109 | * 110 | * If work has already been signaled by ::worker_wake but has not been started 111 | * when this function is called, this function will block until that work is 112 | * finished. 113 | */ 114 | int worker_join(struct worker_handle *wh); 115 | 116 | /*! 117 | * @brief Starts the target worker instance 118 | * 119 | * @param[in,out] wh Target worker instance 120 | * 121 | * @returns 0 on success, negative ERRNO value on failure 122 | */ 123 | int worker_start(struct worker_handle *wh); 124 | 125 | /*! 126 | * @brief Wait for the worker to become idle 127 | * 128 | * @param[in,out] wh Target worker instance 129 | * 130 | * @returns 0 when the worker becomes idle, negative ERRNO value on failure 131 | */ 132 | int worker_wait_idle(struct worker_handle *wh); 133 | 134 | /*! 135 | * @brief Signal to the worker that work is available 136 | * 137 | * @param[in,out] wh Target worker instance 138 | * 139 | * @returns 0 on success, negative ERRNO value on failure 140 | * 141 | * Barring abnormal termination of the backing thread, a successful call to 142 | * this function guarantees that work will be performed, even if the work does 143 | * not begin before a subsequent call to ::worker_join. 144 | */ 145 | int worker_wake(struct worker_handle *wh); 146 | 147 | #endif /* WORKER_H_ */ 148 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Source configuration 3 | # 4 | 5 | if(UNIX) 6 | set(OPENELP_PLATFORM_FILES 7 | ${OPENELP_SOURCE_DIR}/mutex_posix.c 8 | ${OPENELP_SOURCE_DIR}/thread_posix.c 9 | ) 10 | set(OPENELP_ICON_RESOURCE) 11 | set(OPENELP_VERSION_RESOURCE) 12 | elseif(WIN32) 13 | set(OPENELP_PLATFORM_FILES 14 | ${CMAKE_CURRENT_BINARY_DIR}/eventlog/log_eventlog_messages.rc 15 | ${OPENELP_SOURCE_DIR}/conn_wsa_errno.c 16 | ${OPENELP_SOURCE_DIR}/mutex_win.c 17 | ${OPENELP_SOURCE_DIR}/thread_win.c 18 | ) 19 | set(OPENELP_ICON_RESOURCE 20 | "${CMAKE_CURRENT_BINARY_DIR}/eventlog/icon.rc" 21 | ) 22 | set(OPENELP_VERSION_RESOURCE 23 | "${OPENELP_SOURCE_DIR}/version.rc" 24 | ) 25 | else() 26 | message(ERROR "Unsupported platform") 27 | endif() 28 | 29 | if(OPENELP_USE_OPENSSL) 30 | set(OPENELP_MD5_FILES) 31 | else() 32 | set(OPENELP_MD5_FILES ${OPENELP_SOURCE_DIR}/md5.c) 33 | endif() 34 | 35 | # 36 | # Targets 37 | # 38 | 39 | add_library(openelp_objects OBJECT 40 | ${OPENELP_SOURCE_DIR}/conf.c 41 | ${OPENELP_SOURCE_DIR}/conn.c 42 | ${OPENELP_SOURCE_DIR}/digest.c 43 | ${OPENELP_SOURCE_DIR}/log.c 44 | ${OPENELP_SOURCE_DIR}/pearson.c 45 | ${OPENELP_SOURCE_DIR}/proxy.c 46 | ${OPENELP_SOURCE_DIR}/proxy_client.c 47 | ${OPENELP_SOURCE_DIR}/proxy_conn.c 48 | ${OPENELP_SOURCE_DIR}/rand.c 49 | ${OPENELP_SOURCE_DIR}/regex.c 50 | ${OPENELP_SOURCE_DIR}/registration.c 51 | ${OPENELP_SOURCE_DIR}/worker.c 52 | ${OPENELP_MD5_FILES} 53 | ${OPENELP_PLATFORM_FILES} 54 | ) 55 | 56 | if(WIN32) 57 | add_custom_command(OUTPUT 58 | "${CMAKE_CURRENT_BINARY_DIR}/eventlog/log_eventlog_messages.h" 59 | "${CMAKE_CURRENT_BINARY_DIR}/eventlog/log_eventlog_messages.rc" 60 | COMMAND mc ARGS 61 | -U "${OPENELP_SOURCE_DIR}/log_eventlog_messages.mc" 62 | -r "${CMAKE_CURRENT_BINARY_DIR}/eventlog" 63 | -h "${CMAKE_CURRENT_BINARY_DIR}/eventlog" 64 | MAIN_DEPENDENCY "${OPENELP_SOURCE_DIR}/log_eventlog_messages.mc" 65 | WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/eventlog" 66 | ) 67 | 68 | file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/eventlog/icon.rc" 69 | "IDI_ICON1 ICON DISCARDABLE \"${OPENELP_DOC_DIR}/icons/windows.ico\"" 70 | ) 71 | 72 | target_include_directories(openelp_objects 73 | PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/eventlog" 74 | ) 75 | endif() 76 | 77 | if(NOT WIN32 AND BUILD_SHARED_LIBS) 78 | target_compile_options(openelp_objects PRIVATE -fPIC) 79 | endif() 80 | 81 | add_library(openelp 82 | $ 83 | ${OPENELP_VERSION_RESOURCE}) 84 | 85 | add_executable(openelpd 86 | "${OPENELP_SOURCE_DIR}/proxyd.c" 87 | ${OPENELP_ICON_RESOURCE} 88 | ${OPENELP_VERSION_RESOURCE}) 89 | 90 | if(WIN32) 91 | add_executable(openelp_service 92 | ${OPENELP_SOURCE_DIR}/service.c 93 | ${OPENELP_VERSION_RESOURCE}) 94 | endif() 95 | 96 | # 97 | # Includes 98 | # 99 | 100 | target_include_directories(openelp_objects PUBLIC 101 | ${OPENELP_INCLUDE_DIR} 102 | ) 103 | target_include_directories(openelp_objects PRIVATE 104 | ${PCRE_INCLUDE_DIRS} 105 | ${OPENSSL_INCLUDE_DIRS} 106 | ) 107 | target_include_directories(openelpd PRIVATE 108 | ${OPENELP_INCLUDE_DIR} 109 | ) 110 | 111 | if(WIN32) 112 | target_include_directories(openelp_service PRIVATE 113 | ${OPENELP_INCLUDE_DIR} 114 | ) 115 | endif() 116 | 117 | # 118 | # Libraries 119 | # 120 | 121 | target_link_libraries(openelp PRIVATE ${PCRE_LIBRARIES}) 122 | 123 | if(UNIX) 124 | target_link_libraries(openelp PRIVATE pthread) 125 | elseif(WIN32) 126 | target_link_libraries(openelp PRIVATE ws2_32 advapi32) 127 | endif() 128 | 129 | if(OPENELP_USE_OPENSSL) 130 | target_link_libraries(openelp PRIVATE ${OPENSSL_LIBRARIES}) 131 | endif() 132 | 133 | target_link_libraries(openelpd PRIVATE openelp) 134 | 135 | if(WIN32) 136 | target_link_libraries(openelp_service PRIVATE openelp) 137 | endif() 138 | 139 | # 140 | # Properties 141 | # 142 | 143 | set_target_properties(openelp PROPERTIES 144 | SOVERSION ${OPENELP_MAJOR_VERSION} 145 | VERSION ${OPENELP_MAJOR_VERSION}.${OPENELP_MINOR_VERSION}.${OPENELP_PATCH_VERSION} 146 | ) 147 | 148 | if(WIN32) 149 | target_compile_options(openelp_objects PRIVATE 150 | "-DOPENELP_API=__declspec(dllexport)" 151 | ) 152 | else() 153 | target_compile_options(openelp_objects PRIVATE 154 | "-DOPENELP_API=__attribute__((__visibility__(\"default\")))" 155 | "-fvisibility=hidden" 156 | ) 157 | endif() 158 | 159 | if(OPENELP_BUNDLE_PCRE) 160 | add_dependencies(openelp_objects pcre) 161 | endif() 162 | 163 | # 164 | # Install 165 | # 166 | 167 | if(WIN32) 168 | install(TARGETS openelp 169 | RUNTIME DESTINATION bin 170 | COMPONENT libs 171 | ) 172 | 173 | install(TARGETS openelp 174 | ARCHIVE DESTINATION "${LIB_INSTALL_DIR}" 175 | COMPONENT devel 176 | ) 177 | else() 178 | install(TARGETS openelp 179 | ARCHIVE DESTINATION "${LIB_INSTALL_DIR}" 180 | LIBRARY DESTINATION "${LIB_INSTALL_DIR}" 181 | RUNTIME DESTINATION bin 182 | COMPONENT libs 183 | ) 184 | endif() 185 | 186 | install(TARGETS openelpd 187 | RUNTIME DESTINATION "${BIN_INSTALL_DIR}" 188 | COMPONENT app 189 | ) 190 | 191 | if(WIN32) 192 | install(TARGETS openelp_service 193 | RUNTIME DESTINATION "${BIN_INSTALL_DIR}" 194 | COMPONENT service 195 | ) 196 | endif() 197 | -------------------------------------------------------------------------------- /src/digest.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file digest.c 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Digest generation implementation 45 | */ 46 | 47 | #include 48 | #include 49 | 50 | #ifdef _WIN32 51 | # include 52 | #else 53 | # include 54 | #endif 55 | 56 | #include "digest.h" 57 | #include "md5.h" 58 | 59 | /*! 60 | * @brief Converts a 8-bit value to a base 16 string 61 | * 62 | * @param[in] data Numeric value to convert 63 | * @param[out] result Resulting ASCII characters 64 | */ 65 | static void digest_to_hex8(uint8_t data, char result[2]); 66 | 67 | /*! 68 | * @brief Converts a 8-bit value to a base 16 string with uppercase letters 69 | * 70 | * @param[in] data Numeric value to convert 71 | * @param[out] result Resulting ASCII characters 72 | */ 73 | static void digest_to_hex8_u(uint8_t data, char result[2]); 74 | 75 | /*! 76 | * @brief Converts a base 16 string to a 4-bit value 77 | * 78 | * @param[in] data ASCII character to convert 79 | * @returns Resulting numeric value 80 | */ 81 | static uint8_t hex4_to_digest(char data); 82 | 83 | /*! 84 | * @brief Converts a base 16 string to an 8-bit value 85 | * 86 | * @param[in] data ASCII characters to convert 87 | * @returns Resulting numeric value 88 | */ 89 | static uint8_t hex8_to_digest(const char data[2]); 90 | 91 | void digest_get(const uint8_t *data, unsigned int len, 92 | uint8_t result[DIGEST_LEN]) 93 | { 94 | MD5_CTX ctx; 95 | 96 | MD5_Init(&ctx); 97 | 98 | MD5_Update(&ctx, data, len); 99 | 100 | MD5_Final((unsigned char *)result, &ctx); 101 | } 102 | 103 | static void digest_to_hex8(uint8_t data, char result[2]) 104 | { 105 | static const char lookup[16] = { 106 | '0', '1', '2', '3', '4', '5', '6', '7', 107 | '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 108 | }; 109 | 110 | result[0] = lookup[data / 16]; 111 | result[1] = lookup[data % 16]; 112 | } 113 | 114 | static void digest_to_hex8_u(uint8_t data, char result[2]) 115 | { 116 | static const char lookup[16] = { 117 | '0', '1', '2', '3', '4', '5', '6', '7', 118 | '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 119 | }; 120 | 121 | result[0] = lookup[data / 16]; 122 | result[1] = lookup[data % 16]; 123 | } 124 | 125 | void digest_to_hex32(uint32_t data, char result[8]) 126 | { 127 | digest_to_hex8(data >> 0x18 & 0xff, &result[0]); 128 | digest_to_hex8(data >> 0x10 & 0xff, &result[2]); 129 | digest_to_hex8(data >> 0x8 & 0xff, &result[4]); 130 | digest_to_hex8(data & 0xff, &result[6]); 131 | } 132 | 133 | void digest_to_str(const uint8_t md5[DIGEST_LEN], 134 | char result[2 * DIGEST_LEN + 1]) 135 | { 136 | size_t i; 137 | 138 | for (i = 0; i < DIGEST_LEN; i++, result += 2) 139 | digest_to_hex8_u(md5[i], result); 140 | } 141 | 142 | static uint8_t hex4_to_digest(char data) 143 | { 144 | char base = '0'; 145 | 146 | if (data > '9') { 147 | if (data > 'F') { 148 | if (data > 'f') 149 | return 0; 150 | base = 'a' - 10; 151 | } else { 152 | base = 'A' - 10; 153 | } 154 | } 155 | 156 | if (data < base) 157 | return 0; 158 | 159 | return data - base; 160 | } 161 | 162 | static uint8_t hex8_to_digest(const char data[2]) 163 | { 164 | return (hex4_to_digest(data[0]) * 16) + hex4_to_digest(data[1]); 165 | } 166 | 167 | uint32_t hex32_to_digest(const char data[8]) 168 | { 169 | union { 170 | uint32_t value; 171 | uint8_t parts[4]; 172 | } result; 173 | 174 | result.parts[0] = hex8_to_digest(&data[0]); 175 | result.parts[1] = hex8_to_digest(&data[2]); 176 | result.parts[2] = hex8_to_digest(&data[4]); 177 | result.parts[3] = hex8_to_digest(&data[6]); 178 | 179 | return ntohl(result.value); 180 | } 181 | -------------------------------------------------------------------------------- /include/log.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file log.h 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Internal API for message logging 45 | */ 46 | 47 | #ifndef LOG_H_ 48 | #define LOG_H_ 49 | 50 | #include 51 | #include 52 | 53 | #include "openelp/openelp.h" 54 | 55 | /*! 56 | * @brief Represents an instance of logging infrastructure 57 | * 58 | * This struct should be initialized to zero before being used. The private data 59 | * should be initialized using the ::log_init function, and subsequently 60 | * freed by ::log_free when the logging infrastructure is no longer needed. 61 | */ 62 | struct log_handle { 63 | /*! Private data - used internally by log functions */ 64 | void *priv; 65 | 66 | /*! Severigy threshold for reporting log messages */ 67 | uint32_t level; 68 | 69 | /*! Logging facility used to report logging event messages */ 70 | uint32_t medium; 71 | }; 72 | 73 | /*! 74 | * @brief Closes the current log medium by switching to ::LOG_MEDIUM_NONE 75 | * 76 | * @param[in,out] log Target logging infrastructure instance 77 | */ 78 | void log_close(struct log_handle *log); 79 | 80 | /*! 81 | * @brief Frees data allocated by ::log_init 82 | * 83 | * @param[in,out] log Target logging infrastructure instance 84 | */ 85 | void log_free(struct log_handle *log); 86 | 87 | /*! 88 | * @brief Identify the current OpenELP version to the current log medium 89 | * 90 | * @param[in] log Target logging infrastructure instance 91 | */ 92 | void log_ident(struct log_handle *log); 93 | 94 | /*! 95 | * @brief Initializes the private data in a ::log_handle 96 | * 97 | * @param[in,out] log Target logging infrastructure instance 98 | * 99 | * @returns 0 on success, negative ERRNO value on failure 100 | */ 101 | int log_init(struct log_handle *log); 102 | 103 | /*! 104 | * @brief Converts the given log medium value to a static string representation 105 | * 106 | * @param[in] medium Log medium value to convert 107 | * 108 | * @returns Pointer to a null-terminated static const string 109 | */ 110 | const char *log_medium_to_str(enum LOG_MEDIUM medium); 111 | 112 | /*! 113 | * @brief Opens the log infrastructure by switching to ::LOG_MEDIUM_STDOUT 114 | * 115 | * @param[in,out] log Target logging infrastructure instance 116 | * 117 | * @returns 0 on success, negative ERRNO value on failure 118 | */ 119 | int log_open(struct log_handle *log); 120 | 121 | /*! 122 | * @brief Logs the given message to the current logging facility 123 | * 124 | * @param[in] log Target logging infrastructure instance 125 | * @param[in] lvl Message importance level 126 | * @param[in] fmt String format of message 127 | * @param[in] ... Arguments for format specification 128 | */ 129 | void log_printf(struct log_handle *log, enum LOG_LEVEL lvl, 130 | const char *fmt, ...); 131 | 132 | /*! 133 | * @brief Changes the target logging medium 134 | * 135 | * @param[in,out] log Target logging infrastructure instance 136 | * @param[in] medium New logging medium to use 137 | * @param[in] target Medium target, where appropriate 138 | * 139 | * @returns 0 on success, negative ERRNO value on failure 140 | */ 141 | int log_select_medium(struct log_handle *log, enum LOG_MEDIUM medium, 142 | const char *target); 143 | 144 | /*! 145 | * @brief Logs the given message to the current logging facility 146 | * 147 | * @param[in] log Target logging infrastructure instance 148 | * @param[in] lvl Message importance level 149 | * @param[in] fmt String format of message 150 | * @param[in] args Arguments for format specification 151 | */ 152 | void log_vprintf(struct log_handle *log, enum LOG_LEVEL lvl, 153 | const char *fmt, va_list args); 154 | 155 | #endif /* LOG_H_ */ 156 | -------------------------------------------------------------------------------- /src/mutex_win.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file mutex_win.c 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Mutex implementation for Windows 45 | */ 46 | 47 | #include 48 | #include 49 | #include 50 | 51 | #include 52 | #include 53 | 54 | #include "mutex.h" 55 | 56 | /*! 57 | * @brief Private data for an instance of a Windows thread 58 | */ 59 | struct mutex_priv { 60 | /*! Represents a slim reader/writer lock on Windows */ 61 | SRWLOCK lock; 62 | }; 63 | 64 | /*! 65 | * @brief Private data for an instance of a Windows condition variable 66 | */ 67 | struct condvar_priv { 68 | /*! Represents a condition variable on Windows */ 69 | CONDITION_VARIABLE cond; 70 | }; 71 | 72 | int mutex_init(struct mutex_handle *mutex) 73 | { 74 | struct mutex_priv *priv = mutex->priv; 75 | 76 | if (priv == NULL) { 77 | priv = calloc(1, sizeof(*priv)); 78 | if (priv == NULL) 79 | return -ENOMEM; 80 | 81 | mutex->priv = priv; 82 | } 83 | 84 | InitializeSRWLock(&priv->lock); 85 | 86 | return 0; 87 | } 88 | 89 | int mutex_lock(struct mutex_handle *mutex) 90 | { 91 | struct mutex_priv *priv = mutex->priv; 92 | 93 | AcquireSRWLockExclusive(&priv->lock); 94 | 95 | return 0; 96 | } 97 | 98 | int mutex_lock_shared(struct mutex_handle *mutex) 99 | { 100 | struct mutex_priv *priv = mutex->priv; 101 | 102 | AcquireSRWLockShared(&priv->lock); 103 | 104 | return 0; 105 | } 106 | 107 | int mutex_unlock(struct mutex_handle *mutex) 108 | { 109 | struct mutex_priv *priv = mutex->priv; 110 | 111 | ReleaseSRWLockExclusive(&priv->lock); 112 | 113 | return 0; 114 | } 115 | 116 | int mutex_unlock_shared(struct mutex_handle *mutex) 117 | { 118 | struct mutex_priv *priv = mutex->priv; 119 | 120 | ReleaseSRWLockShared(&priv->lock); 121 | 122 | return 0; 123 | } 124 | 125 | void mutex_free(struct mutex_handle *mutex) 126 | { 127 | if (mutex->priv != NULL) { 128 | free(mutex->priv); 129 | mutex->priv = NULL; 130 | } 131 | } 132 | 133 | int condvar_init(struct condvar_handle *condvar) 134 | { 135 | struct condvar_priv *priv = condvar->priv; 136 | 137 | if (priv == NULL) { 138 | priv = calloc(1, sizeof(*priv)); 139 | if (priv == NULL) 140 | return -ENOMEM; 141 | 142 | condvar->priv = priv; 143 | } 144 | 145 | InitializeConditionVariable(&priv->cond); 146 | 147 | return 0; 148 | } 149 | 150 | int condvar_wait(struct condvar_handle *condvar, struct mutex_handle *mutex) 151 | { 152 | struct condvar_priv *priv = condvar->priv; 153 | struct mutex_priv *mpriv = mutex->priv; 154 | 155 | if (!SleepConditionVariableSRW(&priv->cond, &mpriv->lock, INFINITE, 0)) 156 | return GetLastError(); 157 | 158 | return 0; 159 | } 160 | 161 | int condvar_wait_time(struct condvar_handle *condvar, 162 | struct mutex_handle *mutex, uint32_t msec) 163 | { 164 | struct condvar_priv *priv = condvar->priv; 165 | struct mutex_priv *mpriv = mutex->priv; 166 | int ret = 0; 167 | 168 | if (!SleepConditionVariableSRW(&priv->cond, &mpriv->lock, msec, 0)) { 169 | ret = GetLastError(); 170 | if (ret == ERROR_TIMEOUT) 171 | ret = 1; 172 | } 173 | 174 | return ret; 175 | } 176 | 177 | int condvar_wake_one(struct condvar_handle *condvar) 178 | { 179 | struct condvar_priv *priv = condvar->priv; 180 | 181 | WakeConditionVariable(&priv->cond); 182 | 183 | return 0; 184 | } 185 | 186 | int condvar_wake_all(struct condvar_handle *condvar) 187 | { 188 | struct condvar_priv *priv = condvar->priv; 189 | 190 | WakeAllConditionVariable(&priv->cond); 191 | 192 | return 0; 193 | } 194 | 195 | void condvar_free(struct condvar_handle *condvar) 196 | { 197 | if (condvar->priv != NULL) { 198 | free(condvar->priv); 199 | condvar->priv = NULL; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /doc/icons/scalable.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | OpenELP Logo 22 | 24 | 27 | 31 | 35 | 36 | 45 | 54 | 63 | 64 | 84 | 86 | 87 | 89 | image/svg+xml 90 | 92 | OpenELP Logo 93 | 95 | Sun Jan 17, 2016 96 | 97 | 98 | Scott K Logan 99 | 100 | 101 | 102 | 104 | 106 | 108 | 110 | 112 | 114 | 115 | 116 | 117 | 122 | 127 | 132 | 133 | 139 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /src/proxy_client.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file proxy_client.c 3 | * 4 | * @copyright 5 | * Copyright © 2020, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Client connection implementation 45 | */ 46 | 47 | #include 48 | #include 49 | #include 50 | 51 | #include "conn.h" 52 | #include "digest.h" 53 | #include "openelp/openelp.h" 54 | #include "proxy_client.h" 55 | 56 | /*! 57 | * @brief Private data for an instance of a client connection 58 | */ 59 | struct proxy_client_priv { 60 | /*! The connection to the proxy server */ 61 | struct conn_handle conn; 62 | }; 63 | 64 | int proxy_client_connect(struct proxy_client_handle *ch) 65 | { 66 | struct proxy_client_priv *priv = ch->priv; 67 | const char *password = ch->password; 68 | uint32_t nonce; 69 | char nonce_str[8]; 70 | uint8_t response[PROXY_PASS_RES_LEN]; 71 | int ret; 72 | 73 | if (password == NULL) 74 | password = "PUBLIC"; 75 | 76 | ret = conn_connect(&priv->conn, ch->host_addr, ch->host_port); 77 | if (ret < 0) 78 | goto proxy_client_connection_exit; 79 | 80 | /* Receive the nonce */ 81 | ret = conn_recv(&priv->conn, (uint8_t *)nonce_str, 82 | sizeof(nonce_str)); 83 | if (ret < 0) 84 | goto proxy_client_connection_exit; 85 | 86 | nonce = hex32_to_digest(nonce_str); 87 | 88 | /* Compute the password response */ 89 | ret = get_password_response(nonce, password, response); 90 | if (ret < 0) 91 | goto proxy_client_connection_exit; 92 | 93 | /* Send the callsign, a newline, and the response */ 94 | if (ch->callsign != NULL) { 95 | ret = conn_send(&priv->conn, (uint8_t *)ch->callsign, 96 | strlen(ch->callsign)); 97 | if (ret < 0) 98 | goto proxy_client_connection_exit; 99 | } 100 | 101 | ret = conn_send(&priv->conn, (uint8_t *)"\n", 1); 102 | if (ret < 0) 103 | goto proxy_client_connection_exit; 104 | 105 | ret = conn_send(&priv->conn, response, PROXY_PASS_RES_LEN); 106 | if (ret < 0) 107 | goto proxy_client_connection_exit; 108 | 109 | /* No news is good news, so we won't know how things went 110 | * until we actually try to process the returning messages 111 | */ 112 | 113 | return 0; 114 | 115 | proxy_client_connection_exit: 116 | conn_close(&priv->conn); 117 | 118 | return ret; 119 | } 120 | 121 | void proxy_client_disconnect(struct proxy_client_handle *ch) 122 | { 123 | struct proxy_client_priv *priv = ch->priv; 124 | 125 | conn_close(&priv->conn); 126 | } 127 | 128 | void proxy_client_free(struct proxy_client_handle *ch) 129 | { 130 | struct proxy_client_priv *priv = ch->priv; 131 | 132 | if (ch->priv != NULL) { 133 | proxy_client_disconnect(ch); 134 | 135 | conn_free(&priv->conn); 136 | 137 | free(ch->priv); 138 | ch->priv = NULL; 139 | } 140 | } 141 | 142 | int proxy_client_init(struct proxy_client_handle *ch) 143 | { 144 | struct proxy_client_priv *priv = ch->priv; 145 | int ret; 146 | 147 | if (priv == NULL) { 148 | priv = calloc(1, sizeof(*priv)); 149 | if (priv == NULL) 150 | return -ENOMEM; 151 | 152 | ch->priv = priv; 153 | } 154 | 155 | priv->conn.type = CONN_TYPE_TCP; 156 | ret = conn_init(&priv->conn); 157 | if (ret < 0) 158 | goto proxy_client_init_exit; 159 | 160 | return 0; 161 | 162 | proxy_client_init_exit: 163 | conn_free(&priv->conn); 164 | 165 | free(ch->priv); 166 | ch->priv = NULL; 167 | 168 | return ret; 169 | } 170 | 171 | int proxy_client_recv(struct proxy_client_handle *ch, struct proxy_msg *msg, 172 | uint8_t *buff, size_t buff_len) 173 | { 174 | struct proxy_client_priv *priv = ch->priv; 175 | int ret; 176 | 177 | ret = conn_recv(&priv->conn, (uint8_t *)msg, sizeof(*msg)); 178 | if (ret >= 0 && msg->size > 0) { 179 | if (msg->size > buff_len) 180 | return -ENOSPC; 181 | ret = conn_recv(&priv->conn, buff, msg->size); 182 | } 183 | 184 | return ret; 185 | } 186 | 187 | int proxy_client_send(struct proxy_client_handle *ch, 188 | const struct proxy_msg *msg, const uint8_t *buff) 189 | { 190 | struct proxy_client_priv *priv = ch->priv; 191 | int ret; 192 | 193 | ret = conn_send(&priv->conn, (uint8_t *)msg, sizeof(*msg)); 194 | if (ret >= 0 && msg->size > 0) 195 | ret = conn_send(&priv->conn, buff, msg->size); 196 | 197 | return ret; 198 | } 199 | -------------------------------------------------------------------------------- /include/proxy_conn.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file proxy_conn.h 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Internal API for proxy client connections 45 | */ 46 | 47 | #ifndef PROXY_CONN_H_ 48 | #define PROXY_CONN_H_ 49 | 50 | #include "conn.h" 51 | 52 | /*! 53 | * @brief Represents an instance of a proxy client connection 54 | * 55 | * This struct should be initialized to zero before being used. The private data 56 | * should be initialized using the ::proxy_conn_init function, and subsequently 57 | * freed by ::proxy_conn_free when the regular expression is no longer needed. 58 | */ 59 | struct proxy_conn_handle { 60 | /*! Private data - used internally by proxy_conn functions */ 61 | void *priv; 62 | 63 | /*! Reference to the parent proxy instance handle */ 64 | struct proxy_handle *ph; 65 | 66 | /*! Null-terminated string containing the source address for client data */ 67 | const char *source_addr; 68 | 69 | /*! Null-terminated struing containing the port number for control packets */ 70 | const char *control_port; 71 | 72 | /*! Null-terminated struing containing the port number for data packets */ 73 | const char *data_port; 74 | 75 | /*! The next ::proxy_conn_handle in the linked list */ 76 | struct proxy_conn_handle *next; 77 | 78 | /*! The pointer to this ::proxy_conn_handle in the linked list */ 79 | struct proxy_conn_handle **prev_ptr; 80 | 81 | /*! The next ::proxy_conn_handle in the linked list by callsign*/ 82 | struct proxy_conn_handle *next_by_call; 83 | 84 | /*! The pointer to this ::proxy_conn_handle in the linked list by callsign */ 85 | struct proxy_conn_handle **prev_by_call_ptr; 86 | }; 87 | 88 | /*! 89 | * @brief Claims a proxy connection for use by the given client 90 | * 91 | * @param[in,out] pc Target proxy client connection instance 92 | * @param[in] conn_client Connection to a client 93 | * @param[in] callsign The authentication callsign given by the client 94 | * @param[in] reconnect_only Non-zero to accept only if the callsign matches 95 | * 96 | * @returns 0 on success, negative ERRNO value on failure 97 | * 98 | * Note that ownership of \p conn_client remains with the caller, but 99 | * \p conn_client must remain valid until the proxy client has finished 100 | * with it. Calling \p proxy_conn_finish should ensure this. 101 | */ 102 | int proxy_conn_accept(struct proxy_conn_handle *pc, 103 | struct conn_handle *conn_client, 104 | const char *callsign, 105 | uint8_t reconnect_only); 106 | 107 | /*! 108 | * @brief Begins an orderly shutdown of all active connections 109 | * 110 | * @param[in,out] pc Target proxy client connection instance 111 | */ 112 | void proxy_conn_drop(struct proxy_conn_handle *pc); 113 | 114 | /*! 115 | * @brief Waits for the proxy_conn to close connections and become idle 116 | * 117 | * @param[in,out] pc Target proxy client connection instance 118 | */ 119 | void proxy_conn_finish(struct proxy_conn_handle *pc); 120 | 121 | /*! 122 | * @brief Frees data allocated by ::proxy_conn_init 123 | * 124 | * @param[in,out] pc Target proxy client connection instance 125 | */ 126 | void proxy_conn_free(struct proxy_conn_handle *pc); 127 | 128 | /*! 129 | * @brief Initializes the private data in a ::proxy_conn_handle 130 | * 131 | * @param[in,out] pc Target proxy client connection instance 132 | * 133 | * @returns 0 on success, negative ERRNO value on failure 134 | */ 135 | int proxy_conn_init(struct proxy_conn_handle *pc); 136 | 137 | /*! 138 | * @brief Determine if the connection is currently in use 139 | * 140 | * @param[in] pc Target proxy client connection instance 141 | * 142 | * @returns 1 if in use, 0 if idle 143 | */ 144 | int proxy_conn_in_use(struct proxy_conn_handle *pc); 145 | 146 | /*! 147 | * @brief Blocking call to process new messages 148 | * 149 | * @param[in,out] pc Target proxy client connection instance 150 | * 151 | * @returns 0 on success, negative ERRNO value on failure 152 | */ 153 | int proxy_conn_process(struct proxy_conn_handle *pc); 154 | 155 | /*! 156 | * @brief Starts the client thread and prepares to accept connections 157 | * 158 | * @param[in,out] pc Target proxy client connection instance 159 | * 160 | * @returns 0 on success, negative ERRNO value on failure 161 | */ 162 | int proxy_conn_start(struct proxy_conn_handle *pc); 163 | 164 | /*! 165 | * @brief Disconnects the connected client and stops the client thread 166 | * 167 | * @param[in,out] pc Target proxy client connection instance 168 | * 169 | * @returns 0 on success, negative ERRNO value on failure 170 | */ 171 | int proxy_conn_stop(struct proxy_conn_handle *pc); 172 | 173 | #endif /* PROXY_CONN_H_ */ 174 | -------------------------------------------------------------------------------- /test/test_e2e.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file test_e2e.c 3 | * 4 | * @copyright 5 | * Copyright © 2020, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief End-to-end test of a proxy connection 45 | */ 46 | 47 | #include 48 | #include 49 | #include 50 | 51 | #include "openelp/openelp.h" 52 | #include "proxy_client.h" 53 | #include "worker.h" 54 | 55 | #if _WIN32 56 | # define strdup _strdup 57 | #endif 58 | 59 | /*! Context for ::proxy_processor */ 60 | struct processor_context 61 | { 62 | /*! Handle to the proxy instance to process messages for */ 63 | struct proxy_handle *ph; 64 | 65 | /*! Return value from the most recent processing run */ 66 | int ret; 67 | }; 68 | 69 | /*! 70 | * @brief Worker function for processing proxy server messages 71 | * 72 | * @param[in,out] wh The worker context 73 | */ 74 | static void proxy_processor(struct worker_handle *wh); 75 | 76 | /*! 77 | * @brief Test basic proxy lifecycle and functions 78 | * 79 | * @returns 0 on success, negative ERRNO value on failure 80 | * 81 | * @test Test basic proxy lifecycle and functions 82 | */ 83 | static int test_proxy_e2e(void); 84 | 85 | /*! 86 | * @brief Main entry point for authorization tests 87 | * 88 | * @returns 0 on success, non-zero value on failure 89 | */ 90 | int main(void); 91 | 92 | int main(void) 93 | { 94 | int ret = 0; 95 | 96 | ret |= test_proxy_e2e(); 97 | 98 | return ret; 99 | } 100 | 101 | static void proxy_processor(struct worker_handle *wh) 102 | { 103 | struct processor_context *ctx = wh->func_ctx; 104 | 105 | ctx->ret = proxy_process(ctx->ph); 106 | } 107 | 108 | static int test_proxy_e2e(void) 109 | { 110 | struct proxy_client_handle client = { 0 }; 111 | struct proxy_client_handle client2 = { 0 }; 112 | struct proxy_handle proxy = { 0 }; 113 | struct worker_handle worker = { 0 }; 114 | struct processor_context ctx = { 0 }; 115 | int ret; 116 | 117 | /* Initialize */ 118 | 119 | ctx.ph = &proxy; 120 | ctx.ret = 0; 121 | worker.func_ptr = proxy_processor; 122 | worker.func_ctx = &ctx; 123 | ret = worker_init(&worker); 124 | if (ret < 0) 125 | goto test_proxy_authorize_exit; 126 | 127 | ret = proxy_init(&proxy); 128 | if (ret < 0) 129 | goto test_proxy_authorize_exit; 130 | 131 | client.callsign = "KM0H"; 132 | client.host_addr = "127.0.0.1"; 133 | client.host_port = "8100"; 134 | client.password = "PUBLIC"; 135 | ret = proxy_client_init(&client); 136 | if (ret < 0) 137 | goto test_proxy_authorize_exit; 138 | 139 | client2.callsign = "KM0H"; 140 | client2.host_addr = "127.0.0.1"; 141 | client2.host_port = "8100"; 142 | client2.password = "PUBLIC"; 143 | ret = proxy_client_init(&client2); 144 | if (ret < 0) 145 | goto test_proxy_authorize_exit; 146 | 147 | /* Start the proxy server */ 148 | 149 | proxy_log_level(&proxy, LOG_LEVEL_WARN); 150 | 151 | proxy.conf.bind_addr = strdup("127.0.0.1"); 152 | proxy.conf.bind_addr_ext = strdup("127.0.0.1"); 153 | proxy.conf.calls_allowed = strdup("^KM0H$"); 154 | proxy.conf.password = strdup("PUBLIC"); 155 | proxy.conf.port = 8100; 156 | ret = proxy_open(&proxy); 157 | if (ret < 0) 158 | goto test_proxy_authorize_exit; 159 | 160 | ret = proxy_start(&proxy); 161 | if (ret < 0) 162 | goto test_proxy_authorize_exit; 163 | 164 | ret = worker_start(&worker); 165 | if (ret < 0) 166 | goto test_proxy_authorize_exit; 167 | 168 | /* Try to connect and authorize */ 169 | 170 | ret = worker_wake(&worker); 171 | if (ret < 0) 172 | goto test_proxy_authorize_exit; 173 | 174 | ret = proxy_client_connect(&client); 175 | if (ret < 0) { 176 | fprintf(stderr, "Failed to connect to the client (%d): %s\n", 177 | -ret, strerror(-ret)); 178 | goto test_proxy_authorize_exit; 179 | } 180 | 181 | ret = worker_wait_idle(&worker); 182 | if (ret < 0) 183 | goto test_proxy_authorize_exit; 184 | 185 | /* Verify that the authorization succeeded */ 186 | if (ctx.ret < 0) { 187 | fprintf(stderr, "Authorization failed (%d): %s\n", 188 | -ctx.ret, strerror(-ctx.ret)); 189 | } 190 | 191 | /* Attempt another connection */ 192 | 193 | ret = worker_wake(&worker); 194 | if (ret < 0) 195 | goto test_proxy_authorize_exit; 196 | 197 | ret = proxy_client_connect(&client2); 198 | if (ret != -EPIPE) { 199 | fprintf(stderr, "Invalid busy signal (%d): %s\n", 200 | -ret, strerror(-ret)); 201 | goto test_proxy_authorize_exit; 202 | } 203 | 204 | ret = worker_wait_idle(&worker); 205 | if (ret < 0) 206 | goto test_proxy_authorize_exit; 207 | 208 | /* Verify that the drop was handled correctly */ 209 | if (ctx.ret < 0) { 210 | fprintf(stderr, "Busy client drop failed (%d): %s\n", 211 | -ctx.ret, strerror(-ctx.ret)); 212 | } 213 | 214 | test_proxy_authorize_exit: 215 | proxy_client_free(&client2); 216 | proxy_client_free(&client); 217 | proxy_free(&proxy); 218 | worker_free(&worker); 219 | 220 | return ret; 221 | } 222 | -------------------------------------------------------------------------------- /include/mutex.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file mutex.h 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Internal API for mutual exclusion objects 45 | */ 46 | 47 | #ifndef MUTEX_H_ 48 | #define MUTEX_H_ 49 | 50 | #include 51 | 52 | /*! 53 | * @brief Represents an instance of a condition variable 54 | * 55 | * This struct should be initialized to zero before being used. The private data 56 | * should be initialized using the ::condvar_init function, and subsequently 57 | * freed by ::condvar_free when the condition variable is no longer needed. 58 | */ 59 | struct condvar_handle { 60 | /*! Private data - used internally by condvar functions */ 61 | void *priv; 62 | }; 63 | 64 | /*! 65 | * @brief Represents an instance of a mutex 66 | * 67 | * This struct should be initialized to zero before being used. The private data 68 | * should be initialized using the ::mutex_init function, and subsequently 69 | * freed by ::mutex_free when the mutex is no longer needed. 70 | */ 71 | struct mutex_handle { 72 | /*! Private data - used internally by mutex functions */ 73 | void *priv; 74 | }; 75 | 76 | /*! 77 | * @brief Frees data allocated by ::condvar_init 78 | * 79 | * @param[in,out] condvar Target condition variable instance 80 | */ 81 | void condvar_free(struct condvar_handle *condvar); 82 | 83 | /*! 84 | * @brief Initializes the private data in a ::condvar_handle 85 | * 86 | * @param[in,out] condvar Target condition variable instance 87 | * 88 | * @returns 0 on success, negative ERRNO value on failure 89 | */ 90 | int condvar_init(struct condvar_handle *condvar); 91 | 92 | /*! 93 | * @brief Unlocks the given mutex and blocks until awoken by ::condvar_wake_all 94 | * or ::condvar_wake_one. 95 | * 96 | * @param[in,out] condvar Target condition variable instance 97 | * @param[in,out] mutex Target mutex instance 98 | * 99 | * @returns 0 on success, negative ERRNO value on failure 100 | */ 101 | int condvar_wait(struct condvar_handle *condvar, struct mutex_handle *mutex); 102 | 103 | /*! 104 | * @brief Unlocks the given mutex and blocks until awoken by ::condvar_wake_all, 105 | * ::condvar_wake_one, or a specified timeout. 106 | * 107 | * @param[in,out] condvar Target condition variable instance 108 | * @param[in,out] mutex Target mutex instance 109 | * @param[in] msec Number of milliseconds to wait before returning 110 | * 111 | * @returns 0 on success, 1 on timeout, negative ERRNO value on failure 112 | */ 113 | int condvar_wait_time(struct condvar_handle *condvar, 114 | struct mutex_handle *mutex, uint32_t msec); 115 | 116 | /*! 117 | * @brief Awakens all calls to ::condvar_wait currently blocked 118 | * 119 | * @param[in,out] condvar Target condition variable instance 120 | * 121 | * @returns 0 on success, negative ERRNO value on failure 122 | */ 123 | int condvar_wake_all(struct condvar_handle *condvar); 124 | 125 | /*! 126 | * @brief Awakens a single blocked call to ::condvar_wait 127 | * 128 | * @param[in,out] condvar Target condition variable instance 129 | * 130 | * @returns 0 on success, negative ERRNO value on failure 131 | */ 132 | int condvar_wake_one(struct condvar_handle *condvar); 133 | 134 | /*! 135 | * @brief Frees data allocated by ::mutex_init 136 | * 137 | * @param[in,out] mutex Target mutex instance 138 | */ 139 | void mutex_free(struct mutex_handle *mutex); 140 | 141 | /*! 142 | * @brief Initializes the private data in a ::mutex_handle 143 | * 144 | * @param[in,out] mutex Target mutex instance 145 | * 146 | * @returns 0 on success, negative ERRNO value on failure 147 | */ 148 | int mutex_init(struct mutex_handle *mutex); 149 | 150 | /*! 151 | * @brief Acquires the exclusive lock on the given mutex, blocking if it is 152 | * already locked exclusively or shared 153 | * 154 | * @param[in,out] mutex Target mutex instance 155 | * 156 | * @returns 0 on success, negative ERRNO value on failure 157 | */ 158 | int mutex_lock(struct mutex_handle *mutex); 159 | 160 | /*! 161 | * @brief Acquires a shared Lock on the given mutex, blocking if it is already 162 | * locked exclusively 163 | * 164 | * @param[in,out] mutex Target mutex instance 165 | * 166 | * @returns 0 on success, negative ERRNO value on failure 167 | */ 168 | int mutex_lock_shared(struct mutex_handle *mutex); 169 | 170 | /*! 171 | * @brief Releases the exclusive lock acquired by ::mutex_lock 172 | * 173 | * @param[in,out] mutex Target mutex instance 174 | * 175 | * @returns 0 on success, negative ERRNO value on failure 176 | */ 177 | int mutex_unlock(struct mutex_handle *mutex); 178 | 179 | /*! 180 | * @brief Releases the shared lock acquired by ::mutex_lock_shared 181 | * 182 | * @param[in,out] mutex Target mutex instance 183 | * 184 | * @returns 0 on success, negative ERRNO value on failure 185 | */ 186 | int mutex_unlock_shared(struct mutex_handle *mutex); 187 | 188 | #endif /* MUTEX_H_ */ 189 | -------------------------------------------------------------------------------- /test/test_conn.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file test_conn.c 3 | * 4 | * @copyright 5 | * Copyright © 2020, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Tests related to network connections 45 | */ 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | 52 | #ifdef _WIN32 53 | # include 54 | # define sleep(sec) Sleep(sec * 1000) 55 | #endif 56 | 57 | #include "conn.h" 58 | #include "thread.h" 59 | 60 | /*! 61 | * @brief Contextual data for a thread which receives some data and terminates. 62 | */ 63 | struct conn_recv_data { 64 | /*! The handle of the connection which receives the data */ 65 | struct conn_handle conn; 66 | 67 | /*! The thread which performs the receive operation */ 68 | struct thread_handle thread; 69 | 70 | /*! The return code of the receive operation */ 71 | int ret; 72 | }; 73 | 74 | /*! 75 | * @brief Thread function which performs a blocking receive 76 | * 77 | * @param[in,out] ctx The thread context 78 | * 79 | * @returns Always returns NULL 80 | */ 81 | static void *conn_recv_func(void *ctx); 82 | 83 | /*! 84 | * @brief Basic test for connection closure during a blocking read 85 | * 86 | * @returns 0 on success, negative ERRNO value on failure 87 | * 88 | * @test Basic test for connection closure during a blocking read 89 | */ 90 | static int test_conn_close(void); 91 | 92 | /*! 93 | * @brief Test for ::conn_set_timeout on a blocking read 94 | * 95 | * @returns 0 on success, negative ERRNO value on failure 96 | * 97 | * @test Test for ::conn_set_timeout on a blocking read 98 | */ 99 | static int test_conn_timeout(void); 100 | 101 | static void *conn_recv_func(void *ctx) 102 | { 103 | struct thread_handle *th = ctx; 104 | struct conn_recv_data *data = th->func_ctx; 105 | uint8_t buff[1]; 106 | 107 | data->ret = conn_recv_any(&data->conn, buff, sizeof(buff), NULL, NULL); 108 | 109 | return NULL; 110 | } 111 | 112 | /*! 113 | * @brief Main entry point for connection tests 114 | * 115 | * @returns 0 on success, non-zero value on failure 116 | */ 117 | int main(void); 118 | 119 | int main(void) 120 | { 121 | int ret = 0; 122 | 123 | ret |= test_conn_close(); 124 | ret |= test_conn_timeout(); 125 | 126 | return ret; 127 | } 128 | 129 | static int test_conn_close(void) 130 | { 131 | int ret; 132 | struct conn_recv_data data; 133 | 134 | memset(&data, 0x0, sizeof(struct conn_recv_data)); 135 | 136 | data.conn.type = CONN_TYPE_UDP; 137 | ret = conn_init(&data.conn); 138 | if (ret < 0) 139 | goto test_conn_blocking_exit; 140 | 141 | ret = conn_listen(&data.conn); 142 | if (ret < 0) 143 | goto test_conn_blocking_exit; 144 | 145 | data.thread.func_ctx = &data; 146 | data.thread.func_ptr = conn_recv_func; 147 | ret = thread_init(&data.thread); 148 | if (ret < 0) 149 | goto test_conn_blocking_exit; 150 | 151 | data.ret = -EINPROGRESS; 152 | 153 | ret = thread_start(&data.thread); 154 | if (ret < 0) 155 | goto test_conn_blocking_exit; 156 | 157 | sleep(1); 158 | 159 | conn_close(&data.conn); 160 | 161 | ret = thread_join(&data.thread); 162 | if (ret < 0) 163 | goto test_conn_blocking_exit; 164 | 165 | switch (data.ret) 166 | { 167 | case -EBADF: 168 | case -EINTR: 169 | case -EPIPE: 170 | ret = 0; 171 | break; 172 | default: 173 | ret = data.ret; 174 | fprintf(stderr, 175 | "Error: Invalid return from conn_recv on close (%d): %s\n", 176 | -ret, strerror(-ret)); 177 | break; 178 | } 179 | 180 | test_conn_blocking_exit: 181 | thread_free(&data.thread); 182 | conn_free(&data.conn); 183 | 184 | return ret; 185 | } 186 | 187 | static int test_conn_timeout(void) 188 | { 189 | int ret; 190 | struct conn_recv_data data; 191 | 192 | memset(&data, 0x0, sizeof(struct conn_recv_data)); 193 | 194 | data.conn.type = CONN_TYPE_UDP; 195 | ret = conn_init(&data.conn); 196 | if (ret < 0) 197 | goto test_conn_blocking_exit; 198 | 199 | ret = conn_listen(&data.conn); 200 | if (ret < 0) 201 | goto test_conn_blocking_exit; 202 | 203 | ret = conn_set_timeout(&data.conn, 500); 204 | if (ret < 0) 205 | goto test_conn_blocking_exit; 206 | 207 | data.thread.func_ctx = &data; 208 | data.thread.func_ptr = conn_recv_func; 209 | ret = thread_init(&data.thread); 210 | if (ret < 0) 211 | goto test_conn_blocking_exit; 212 | 213 | data.ret = -EINPROGRESS; 214 | 215 | ret = thread_start(&data.thread); 216 | if (ret < 0) 217 | goto test_conn_blocking_exit; 218 | 219 | sleep(1); 220 | 221 | conn_close(&data.conn); 222 | 223 | ret = thread_join(&data.thread); 224 | if (ret < 0) 225 | goto test_conn_blocking_exit; 226 | 227 | switch (data.ret) 228 | { 229 | case -EAGAIN: 230 | case -ETIMEDOUT: 231 | ret = 0; 232 | break; 233 | default: 234 | ret = data.ret; 235 | fprintf(stderr, 236 | "Error: Invalid return from conn_recv on close (%d): %s\n", 237 | -ret, strerror(-ret)); 238 | break; 239 | } 240 | 241 | test_conn_blocking_exit: 242 | thread_free(&data.thread); 243 | conn_free(&data.conn); 244 | 245 | return ret; 246 | } 247 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: OpenELP CI 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_dispatch: 6 | pull_request: 7 | push: 8 | branches: 9 | - devel 10 | - main 11 | - release/* 12 | 13 | jobs: 14 | build_and_test: 15 | name: Build and Test 16 | runs-on: ${{matrix.os}} 17 | strategy: 18 | matrix: 19 | os: [macos-latest, ubuntu-latest, windows-latest] 20 | build_type: [Release] 21 | include: 22 | - os: macos-latest 23 | test_target: test 24 | extra_flags: -Werror 25 | - os: ubuntu-latest 26 | test_target: test 27 | extra_flags: -Werror -Wformat-overflow -Wformat-security 28 | - os: windows-latest 29 | test_target: RUN_TESTS 30 | extra_flags: /WX 31 | extra_compiler_flags: >- 32 | /experimental:external 33 | /external:anglebrackets 34 | /external:W0 35 | 36 | steps: 37 | - name: Cache PCRE2 38 | uses: actions/cache@v4 39 | with: 40 | path: ${{runner.workspace}}/build/pcre-prefix/src/pcre2-10.42.tar.gz 41 | key: pcre2-10.42 42 | - name: Clone project 43 | uses: actions/checkout@v4 44 | - name: Get snapshot ID 45 | id: get-snap-id 46 | shell: bash 47 | run: echo "snap_id=git${{github.sha}}" | cut -c-19 >> $GITHUB_OUTPUT 48 | - name: Create build environment 49 | shell: bash 50 | working-directory: ${{runner.workspace}} 51 | run: mkdir -p build 52 | - name: Configure project 53 | working-directory: ${{runner.workspace}}/build 54 | run: >- 55 | cmake ${{github.workspace}} 56 | -Werror=dev 57 | -Werror=deprecated 58 | -DCMAKE_BUILD_TYPE=${{matrix.build_type}} 59 | -DBUILD_TESTING:BOOL=ON 60 | -DBUILD_SHARED_LIBS:BOOL=ON 61 | -DOPENELP_DOC_HTMLHELP:BOOL=OFF 62 | -DOPENELP_BUNDLE_PCRE:BOOL=ON 63 | -DOPENELP_USE_OPENSSL:BOOL=OFF 64 | -DCPACK_GENERATOR=ZIP 65 | -DCPACK_PACKAGE_FILE_NAME=OpenELP-${{runner.os}} 66 | -DCPACK_TOPLEVEL_TAG=${{runner.os}} 67 | -DOPENELP_EXTRA_VERSION=${{steps.get-snap-id.outputs.snap_id}} 68 | -DCMAKE_C_FLAGS="${{matrix.extra_flags}} ${{matrix.extra_compiler_flags}}" 69 | -DCMAKE_EXE_LINKER_FLAGS="${{matrix.extra_flags}}" 70 | -DCMAKE_SHARED_LINKER_FLAGS="${{matrix.extra_flags}}" 71 | -DCMAKE_STATIC_LINKER_FLAGS="${{matrix.extra_flags}}" 72 | - name: Build PCRE2 73 | run: >- 74 | cmake 75 | --build ${{runner.workspace}}/build 76 | --config ${{matrix.build_type}} 77 | -j2 78 | -t pcre 79 | - name: Build project 80 | run: >- 81 | cmake 82 | --build ${{runner.workspace}}/build 83 | --config ${{matrix.build_type}} 84 | -j2 85 | - name: Test project 86 | run: >- 87 | cmake 88 | --build ${{runner.workspace}}/build 89 | --config ${{matrix.build_type}} 90 | -j2 91 | -t ${{matrix.test_target}} 92 | if: ${{matrix.test_target}} 93 | env: 94 | CTEST_OUTPUT_ON_FAILURE: 1 95 | - name: Package project 96 | run: >- 97 | cmake 98 | --build ${{runner.workspace}}/build 99 | --config ${{matrix.build_type}} 100 | -j2 101 | -t package 102 | - name: Upload package 103 | uses: actions/upload-artifact@v4 104 | with: 105 | name: OpenELP-${{runner.os}} 106 | path: ${{runner.workspace}}/build/_CPack_Packages/${{runner.os}}/ZIP/OpenELP-${{runner.os}}/* 107 | 108 | build_documentation: 109 | name: Build Documentation 110 | runs-on: ubuntu-20.04 111 | steps: 112 | - name: Install Prerequisites 113 | run: sudo apt-get install libclang-cpp9 libclang1-9 -y 114 | - name: Cache Doxygen 115 | id: cache-doxygen 116 | uses: actions/cache@v4 117 | with: 118 | path: ${{runner.workspace}}/doxygen 119 | key: doxygen-1.9.7 120 | - name: Download Doxygen 121 | if: steps.cache-doxygen.outputs.cache-hit != 'true' 122 | working-directory: ${{runner.workspace}} 123 | run: | 124 | wget -O - https://www.doxygen.nl/files/doxygen-1.9.7.linux.bin.tar.gz | tar -xz 125 | mv doxygen-1.9.7 doxygen 126 | - name: Clone project 127 | uses: actions/checkout@v4 128 | - name: Create build environment 129 | shell: bash 130 | run: | 131 | mkdir -p ${{runner.workspace}}/build 132 | echo "WARN_AS_ERROR = FAIL_ON_WARNINGS" >> doc/Doxyfile.in 133 | - name: Configure project 134 | working-directory: ${{runner.workspace}}/build 135 | run: >- 136 | cmake ${{github.workspace}} 137 | -Werror=dev 138 | -Werror=deprecated 139 | -DCMAKE_PROGRAM_PATH:PATH="${{runner.workspace}}/doxygen/bin" 140 | -DBUILD_TESTING:BOOL=OFF 141 | -DBUILD_SHARED_LIBS:BOOL=OFF 142 | -DOPENELP_DOC_HTMLHELP:BOOL=OFF 143 | -DOPENELP_BUNDLE_PCRE:BOOL=OFF 144 | -DOPENELP_USE_OPENSSL:BOOL=OFF 145 | -DOPENELP_EXTRA_VERSION=${{steps.get-snap-id.outputs.snap_id}} 146 | -DOPENELP_DOC_INTERNAL=ON 147 | - name: Build documentation 148 | run: >- 149 | cmake 150 | --build ${{runner.workspace}}/build 151 | -j2 152 | -t doc 153 | - name: Upload documentation 154 | uses: actions/upload-artifact@v4 155 | with: 156 | name: OpenELP-HTML-Docs 157 | path: ${{runner.workspace}}/build/doc/html/* 158 | 159 | coverage: 160 | name: Code Coverage 161 | runs-on: ubuntu-20.04 162 | steps: 163 | - name: Clone project 164 | uses: actions/checkout@v4 165 | - name: Create build environment 166 | shell: bash 167 | working-directory: ${{runner.workspace}} 168 | run: mkdir -p build 169 | - name: Configure project 170 | working-directory: ${{runner.workspace}}/build 171 | run: >- 172 | cmake ${{github.workspace}} 173 | -Wno-dev 174 | -DCMAKE_BUILD_TYPE=Debug 175 | -DBUILD_TESTING:BOOL=ON 176 | -DBUILD_SHARED_LIBS:BOOL=ON 177 | -DOPENELP_BUNDLE_PCRE:BOOL=OFF 178 | -DOPENELP_USE_OPENSSL:BOOL=OFF 179 | -DCMAKE_C_FLAGS="--coverage" 180 | - name: Build project 181 | run: >- 182 | cmake 183 | --build ${{runner.workspace}}/build 184 | --config Debug 185 | -j2 186 | - name: Test project 187 | run: >- 188 | cmake 189 | --build ${{runner.workspace}}/build 190 | --config Debug 191 | -j2 192 | -t test 193 | env: 194 | CTEST_OUTPUT_ON_FAILURE: 1 195 | - name: Aggregate coverage 196 | run: >- 197 | find ${{runner.workspace}}/build -name '*.gcda' | 198 | xargs gcov -p -s ${{runner.workspace}} 199 | - name: Upload coverage 200 | uses: codecov/codecov-action@v5 201 | with: 202 | directory: ${{github.workspace}} 203 | token: ${{ secrets.CODECOV_TOKEN }} 204 | working-directory: ${{github.workspace}} 205 | -------------------------------------------------------------------------------- /src/mutex_posix.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file mutex_posix.c 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Mutex implementation for POSIX machines 45 | */ 46 | 47 | #include 48 | #include 49 | #include 50 | 51 | #include 52 | 53 | #include "mutex.h" 54 | 55 | /*! 56 | * @brief Private data for an instance of a POSIX mutex 57 | */ 58 | struct mutex_priv { 59 | /*! Condition variable used to implement the shared lock */ 60 | pthread_cond_t cond; 61 | 62 | /*! POSIX mutex used for exclusive locking */ 63 | pthread_mutex_t lock; 64 | 65 | /*! Number of holders of the shared lock */ 66 | unsigned int readers; 67 | }; 68 | 69 | /*! 70 | * @brief Private data for an instance of a POSIX condition variable 71 | */ 72 | struct condvar_priv { 73 | /*! POSIX condition variable */ 74 | pthread_cond_t cond; 75 | }; 76 | 77 | int mutex_init(struct mutex_handle *mutex) 78 | { 79 | struct mutex_priv *priv = mutex->priv; 80 | int ret; 81 | 82 | if (priv == NULL) { 83 | priv = calloc(1, sizeof(*priv)); 84 | if (priv == NULL) 85 | return -ENOMEM; 86 | 87 | mutex->priv = priv; 88 | } 89 | 90 | ret = pthread_mutex_init(&priv->lock, NULL); 91 | if (ret != 0) { 92 | ret = -ret; 93 | 94 | goto mutex_init_exit; 95 | } 96 | 97 | ret = pthread_cond_init(&priv->cond, NULL); 98 | if (ret != 0) { 99 | ret = -ret; 100 | 101 | goto mutex_init_exit_late; 102 | } 103 | 104 | return 0; 105 | 106 | mutex_init_exit_late: 107 | pthread_mutex_destroy(&priv->lock); 108 | 109 | mutex_init_exit: 110 | free(mutex->priv); 111 | mutex->priv = NULL; 112 | 113 | return ret; 114 | } 115 | 116 | int mutex_lock(struct mutex_handle *mutex) 117 | { 118 | struct mutex_priv *priv = mutex->priv; 119 | int ret; 120 | 121 | ret = pthread_mutex_lock(&priv->lock); 122 | if (ret != 0) 123 | return -ret; 124 | 125 | while (priv->readers > 0) { 126 | ret = pthread_cond_wait(&priv->cond, &priv->lock); 127 | if (ret != 0) { 128 | ret = -ret; 129 | goto mutex_lock_exit; 130 | } 131 | } 132 | 133 | return 0; 134 | 135 | mutex_lock_exit: 136 | pthread_mutex_unlock(&priv->lock); 137 | 138 | return ret; 139 | } 140 | 141 | int mutex_lock_shared(struct mutex_handle *mutex) 142 | { 143 | struct mutex_priv *priv = mutex->priv; 144 | int ret; 145 | 146 | ret = pthread_mutex_lock(&priv->lock); 147 | if (ret != 0) 148 | return -ret; 149 | 150 | priv->readers++; 151 | 152 | return -pthread_mutex_unlock(&priv->lock); 153 | } 154 | 155 | int mutex_unlock(struct mutex_handle *mutex) 156 | { 157 | struct mutex_priv *priv = mutex->priv; 158 | 159 | return pthread_mutex_unlock(&priv->lock); 160 | } 161 | 162 | int mutex_unlock_shared(struct mutex_handle *mutex) 163 | { 164 | struct mutex_priv *priv = mutex->priv; 165 | int ret; 166 | 167 | ret = pthread_mutex_lock(&priv->lock); 168 | if (ret != 0) 169 | return -ret; 170 | 171 | if (--(priv->readers) <= 0) { 172 | ret = pthread_cond_broadcast(&priv->cond); 173 | if (ret != 0) { 174 | ret = -ret; 175 | goto mutex_unlock_shared_exit; 176 | } 177 | } 178 | 179 | return -pthread_mutex_unlock(&priv->lock); 180 | 181 | mutex_unlock_shared_exit: 182 | pthread_mutex_unlock(&priv->lock); 183 | 184 | return ret; 185 | } 186 | 187 | void mutex_free(struct mutex_handle *mutex) 188 | { 189 | if (mutex->priv != NULL) { 190 | struct mutex_priv *priv = mutex->priv; 191 | 192 | pthread_mutex_destroy(&priv->lock); 193 | 194 | pthread_cond_destroy(&priv->cond); 195 | 196 | free(mutex->priv); 197 | mutex->priv = NULL; 198 | } 199 | } 200 | 201 | int condvar_init(struct condvar_handle *condvar) 202 | { 203 | struct condvar_priv *priv = condvar->priv; 204 | int ret; 205 | 206 | if (priv == NULL) { 207 | priv = calloc(1, sizeof(*priv)); 208 | if (priv == NULL) 209 | return -ENOMEM; 210 | 211 | condvar->priv = priv; 212 | } 213 | 214 | ret = pthread_cond_init(&priv->cond, NULL); 215 | if (ret != 0) { 216 | ret = -ret; 217 | 218 | goto condvar_init_exit; 219 | } 220 | 221 | return 0; 222 | 223 | condvar_init_exit: 224 | free(condvar->priv); 225 | condvar->priv = NULL; 226 | 227 | return ret; 228 | } 229 | 230 | int condvar_wait(struct condvar_handle *condvar, struct mutex_handle *mutex) 231 | { 232 | struct condvar_priv *priv = condvar->priv; 233 | struct mutex_priv *mpriv = mutex->priv; 234 | 235 | return -pthread_cond_wait(&priv->cond, &mpriv->lock); 236 | } 237 | 238 | int condvar_wait_time(struct condvar_handle *condvar, 239 | struct mutex_handle *mutex, uint32_t msec) 240 | { 241 | struct condvar_priv *priv = condvar->priv; 242 | struct mutex_priv *mpriv = mutex->priv; 243 | struct timespec abstime; 244 | int ret; 245 | 246 | clock_gettime(CLOCK_REALTIME, &abstime); 247 | abstime.tv_nsec += (msec % 1000) * 1000000; 248 | abstime.tv_sec += (msec / 1000) + (abstime.tv_nsec / 1000000000); 249 | abstime.tv_nsec %= 1000000000; 250 | 251 | ret = pthread_cond_timedwait(&priv->cond, &mpriv->lock, &abstime); 252 | return ret == ETIMEDOUT ? 1 : -ret; 253 | } 254 | 255 | int condvar_wake_one(struct condvar_handle *condvar) 256 | { 257 | struct condvar_priv *priv = condvar->priv; 258 | 259 | return -pthread_cond_signal(&priv->cond); 260 | } 261 | 262 | int condvar_wake_all(struct condvar_handle *condvar) 263 | { 264 | struct condvar_priv *priv = condvar->priv; 265 | 266 | return -pthread_cond_broadcast(&priv->cond); 267 | } 268 | 269 | void condvar_free(struct condvar_handle *condvar) 270 | { 271 | if (condvar->priv != NULL) { 272 | struct condvar_priv *priv = condvar->priv; 273 | 274 | pthread_cond_destroy(&priv->cond); 275 | 276 | free(condvar->priv); 277 | condvar->priv = NULL; 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(openelp C) 3 | 4 | # 5 | # Installation directories 6 | # 7 | 8 | set(SHARE_INSTALL_PREFIX "share" CACHE PATH 9 | "Installation prefix for all application data" 10 | ) 11 | set(BIN_INSTALL_DIR "bin" CACHE PATH 12 | "Installation directory for all binaries" 13 | ) 14 | set(INCLUDE_INSTALL_DIR "include" CACHE PATH 15 | "Installation directory for all header files" 16 | ) 17 | set(LIB_SUFFIX "" CACHE STRING 18 | "Suffix appended for the library installation directory" 19 | ) 20 | set(LIB_INSTALL_DIR "lib${LIB_SUFFIX}" CACHE PATH 21 | "Installation directory for all shared libraries" 22 | ) 23 | if(WIN32) 24 | set(SYSCONF_INSTALL_DIR "./" CACHE PATH 25 | "Installation directory for all configuration files" 26 | ) 27 | else() 28 | set(SYSCONF_INSTALL_DIR "etc" CACHE PATH 29 | "Installation directory for all configuration files" 30 | ) 31 | endif() 32 | set(MAN_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/man" CACHE PATH 33 | "Installation directory for all manuals" 34 | ) 35 | set(SYSTEMD_SERVICES_INSTALL_DIR "lib/systemd/system" CACHE PATH 36 | "Installation directory for systemd services" 37 | ) 38 | 39 | # 40 | # Configuration Variables 41 | # 42 | 43 | # Make sure we have CMAKE_BUILD_TYPE set to something 44 | if(NOT CMAKE_BUILD_TYPE) 45 | list(GET CMAKE_CONFIGURATION_TYPES 0 CMAKE_BUILD_TYPE) 46 | endif() 47 | 48 | set(OPENELP_EXTRA_VERSION "dev" CACHE STRING 49 | "Extra value suffixed to the version number" 50 | ) 51 | 52 | if(IS_ABSOLUTE ${SYSCONF_INSTALL_DIR}) 53 | file(TO_CMAKE_PATH 54 | "${SYSCONF_INSTALL_DIR}/ELProxy.conf" 55 | OPENELP_CONFIG_HINT_DEFAULT) 56 | elseif(IS_ABSOLUTE ${BIN_INSTALL_DIR}) 57 | file(RELATIVE_PATH OPENELP_CONFIG_HINT_DEFAULT 58 | "${BIN_INSTALL_DIR}" 59 | "${CMAKE_PREFIX_PATH}/${SYSCONF_INSTALL_DIR}/ELProxy.conf") 60 | else() 61 | file(RELATIVE_PATH OPENELP_CONFIG_HINT_DEFAULT 62 | "${CMAKE_PREFIX_PATH}/${BIN_INSTALL_DIR}" 63 | "${CMAKE_PREFIX_PATH}/${SYSCONF_INSTALL_DIR}/ELProxy.conf") 64 | endif() 65 | 66 | set(BUILD_SHARED_LIBS ON CACHE BOOL 67 | "Indicates that any libraries which are produced should be shared (.dll/.so)" 68 | ) 69 | set(OPENELP_DOC_INTERNAL FALSE CACHE BOOL 70 | "Include internal code when documenting API" 71 | ) 72 | set(OPENELP_USE_OPENSSL FALSE CACHE BOOL 73 | "Use OpenSSL for MD5 computation instead of bundled md5.c" 74 | ) 75 | set(OPENELP_CONFIG_HINT ${OPENELP_CONFIG_HINT_DEFAULT} CACHE PATH 76 | "Hint path when searching for the proxy configuration file at runtime" 77 | ) 78 | if(WIN32) 79 | set(OPENELP_BUNDLE_PCRE TRUE CACHE BOOL 80 | "Download and compile PCRE2 instead of using the system's libpcre2-32" 81 | ) 82 | set(OPENELP_USE_EVENTLOG TRUE CACHE BOOL 83 | "Enable support for logging to the Windows Event Log" 84 | ) 85 | set(OPENELP_USE_SYSLOG FALSE CACHE BOOL 86 | "Enable support for logging to SYSLOG" 87 | ) 88 | set(OPENELP_DOC_HTMLHELP TRUE CACHE BOOL 89 | "Enable generation of Windows CHM help" 90 | ) 91 | else() 92 | if(APPLE) 93 | set(OPENELP_BUNDLE_PCRE TRUE CACHE BOOL 94 | "Download and compile PCRE2 instead of using the system's libpcre2-32" 95 | ) 96 | else() 97 | set(OPENELP_BUNDLE_PCRE FALSE CACHE BOOL 98 | "Download and compile PCRE2 instead of using the system's libpcre2-32" 99 | ) 100 | endif() 101 | set(OPENELP_USE_EVENTLOG FALSE CACHE BOOL 102 | "Enable support for logging to the Windows Event Log" 103 | ) 104 | set(OPENELP_USE_SYSLOG TRUE CACHE BOOL 105 | "Enable support for logging to SYSLOG" 106 | ) 107 | set(OPENELP_DOC_HTMLHELP FALSE CACHE BOOL 108 | "Enable generation of Windows CHM help" 109 | ) 110 | endif() 111 | 112 | # 113 | # Dependencies 114 | # 115 | 116 | if(OPENELP_BUNDLE_PCRE) 117 | include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/BundlePCRE.cmake") 118 | else() 119 | find_package(PkgConfig REQUIRED) 120 | pkg_check_modules(PCRE REQUIRED libpcre2-8) 121 | endif() 122 | 123 | if(OPENELP_USE_OPENSSL) 124 | find_package(OpenSSL REQUIRED) 125 | endif() 126 | 127 | if(OPENELP_DOC_HTMLHELP) 128 | find_program(OPENELP_DOC_HTMLHELP_PATH hhc 129 | PATHS 130 | "$ENV{ProgramW6432}\\HTML Help Workshop" 131 | "$ENV{ProgramFiles}\\HTML Help Workshop" 132 | DOC "HTML Help Compiler Command" 133 | ) 134 | if(NOT OPENELP_DOC_HTMLHELP_PATH) 135 | message(FATAL_ERROR "Failed to find HTML Help Compiler (hhc)") 136 | endif() 137 | endif() 138 | 139 | if(MSVC) 140 | set(CMAKE_INSTALL_SYSTEM_RUNTIME_COMPONENT runtime) 141 | include(InstallRequiredSystemLibraries) 142 | endif() 143 | 144 | # 145 | # Variables 146 | # 147 | 148 | set(OPENELP_DIR "${CMAKE_CURRENT_SOURCE_DIR}") 149 | set(OPENELP_DOC_DIR "${OPENELP_DIR}/doc") 150 | set(OPENELP_INCLUDE_DIR "${OPENELP_DIR}/include") 151 | set(OPENELP_PACKAGE_DIR "${OPENELP_DIR}/package") 152 | set(OPENELP_SOURCE_DIR "${OPENELP_DIR}/src") 153 | set(OPENELP_TEST_DIR "${OPENELP_DIR}/test") 154 | 155 | set(OPENELP_MAJOR_VERSION 0) 156 | set(OPENELP_MINOR_VERSION 9) 157 | set(OPENELP_PATCH_VERSION 4) 158 | set(OPENELP_VERSION 159 | ${OPENELP_MAJOR_VERSION}.${OPENELP_MINOR_VERSION}.${OPENELP_PATCH_VERSION}) 160 | if(OPENELP_EXTRA_VERSION) 161 | set(OPENELP_VERSION "${OPENELP_VERSION}-${OPENELP_EXTRA_VERSION}") 162 | endif() 163 | 164 | # 165 | # Flags 166 | # 167 | 168 | add_compile_options( 169 | -DOPENELP_MAJOR_VERSION=${OPENELP_MAJOR_VERSION} 170 | -DOPENELP_MINOR_VERSION=${OPENELP_MINOR_VERSION} 171 | -DOPENELP_PATCH_VERSION=${OPENELP_PATCH_VERSION} 172 | -DOPENELP_VERSION=${OPENELP_VERSION} 173 | ) 174 | 175 | if(OPENELP_EXTRA_VERSION) 176 | add_compile_options( 177 | -DOPENELP_EXTRA_VERSION=${OPENELP_EXTRA_VERSION} 178 | ) 179 | endif() 180 | 181 | if(OPENELP_CONFIG_HINT) 182 | add_compile_options( 183 | -DOPENELP_CONFIG_HINT=${OPENELP_CONFIG_HINT} 184 | ) 185 | endif() 186 | 187 | add_compile_options( 188 | -DPCRE2_CODE_UNIT_WIDTH=8 189 | ) 190 | 191 | if(OPENELP_BUNDLE_PCRE) 192 | add_compile_options( 193 | -DPCRE2_STATIC=1 194 | ) 195 | endif() 196 | 197 | if(OPENELP_USE_EVENTLOG) 198 | add_compile_options( 199 | -DHAVE_EVENTLOG=1 200 | ) 201 | endif() 202 | 203 | if(OPENELP_USE_SYSLOG) 204 | add_compile_options( 205 | -DHAVE_SYSLOG=1 206 | ) 207 | endif() 208 | 209 | if(OPENELP_USE_OPENSSL) 210 | add_compile_options( 211 | -DHAVE_OPENSSL=1 212 | ) 213 | endif() 214 | 215 | if(WIN32) 216 | add_compile_options( 217 | /W3 218 | /D_CRT_SECURE_NO_WARNINGS 219 | /DWIN32_LEAN_AND_MEAN 220 | ) 221 | else() 222 | add_compile_options( 223 | -Wall 224 | -Wextra 225 | -Wunused-macros 226 | -Wunreachable-code 227 | ) 228 | include(CheckCCompilerFlag) 229 | check_c_compiler_flag(-Wpedantic HAVE_W_PEDANTIC) 230 | if(HAVE_W_PEDANTIC) 231 | add_compile_options(-Wpedantic) 232 | endif() 233 | check_c_compiler_flag(-Wint-conversion HAVE_W_INT_CONVERSION) 234 | if(HAVE_W_INT_CONVERSION) 235 | add_compile_options(-Wint-conversion) 236 | endif() 237 | endif() 238 | 239 | # Default to C90 240 | if(NOT CMAKE_C_STANDARD) 241 | set(CMAKE_C_STANDARD 90) 242 | endif() 243 | 244 | # 245 | # Link Dirs 246 | # 247 | 248 | link_directories( 249 | ${PCRE_LIBRARY_DIRS} 250 | ${OPENSSL_LIB_DIRS} 251 | ) 252 | 253 | # 254 | # Subdirs 255 | # 256 | 257 | add_subdirectory(${OPENELP_DOC_DIR}) 258 | add_subdirectory(${OPENELP_INCLUDE_DIR}) 259 | add_subdirectory(${OPENELP_SOURCE_DIR}) 260 | 261 | include(CTest) 262 | if(BUILD_TESTING) 263 | add_subdirectory(${OPENELP_TEST_DIR}) 264 | endif() 265 | 266 | include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/Package.cmake") 267 | -------------------------------------------------------------------------------- /src/worker.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file worker.c 3 | * 4 | * @copyright 5 | * Copyright © 2020, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Worker implementation 45 | */ 46 | 47 | #include 48 | #include 49 | #include 50 | 51 | #include "worker.h" 52 | 53 | /*! 54 | * @brief The current state of a worker instance 55 | */ 56 | enum worker_state { 57 | /*! The worker is not running */ 58 | WORKER_STOPPED = 0x0, 59 | 60 | /*! The worker is busy, but will shut down */ 61 | WORKER_STOPPING, 62 | 63 | /*! The worker will process work and then shut down */ 64 | WORKER_STOPPING_AFTER_WORK, 65 | 66 | /*! The worker is currently processing work */ 67 | WORKER_BUSY, 68 | 69 | /*! The worker will process work soon */ 70 | WORKER_SIGNALED, 71 | 72 | /*! The worker is waiting for new work */ 73 | WORKER_IDLE, 74 | 75 | /*! The worker is starting up */ 76 | WORKER_STARTING 77 | }; 78 | 79 | /*! 80 | * @brief Private data for an instance of a worker 81 | */ 82 | struct worker_priv { 83 | /*! Used to signal new work available */ 84 | struct condvar_handle condvar; 85 | 86 | /*! Used to signal that the worker is idle */ 87 | struct condvar_handle condvar_idle; 88 | 89 | /*! Mutex for protecting worker_priv::state */ 90 | struct mutex_handle mutex; 91 | 92 | /*! Execution thread */ 93 | struct thread_handle thread; 94 | 95 | /*! The current state of the worker */ 96 | enum worker_state state; 97 | }; 98 | 99 | /*! 100 | * @brief Thread function which services a worker's work signals 101 | * 102 | * @param[in,out] ctx The thread context 103 | * 104 | * @returns Always returns NULL 105 | */ 106 | static void *worker_func(void *ctx); 107 | 108 | static void *worker_func(void *ctx) 109 | { 110 | struct thread_handle *th = ctx; 111 | struct worker_handle *wh = th->func_ctx; 112 | struct worker_priv *priv = wh->priv; 113 | 114 | mutex_lock(&priv->mutex); 115 | 116 | while (priv->state > WORKER_STOPPING) { 117 | while (priv->state == WORKER_SIGNALED || 118 | priv->state == WORKER_STOPPING_AFTER_WORK) { 119 | if (priv->state == WORKER_STOPPING_AFTER_WORK) 120 | priv->state = WORKER_STOPPING; 121 | else 122 | priv->state = WORKER_BUSY; 123 | mutex_unlock(&priv->mutex); 124 | wh->func_ptr(wh); 125 | mutex_lock(&priv->mutex); 126 | } 127 | 128 | if (priv->state <= WORKER_STOPPING) 129 | break; 130 | 131 | priv->state = WORKER_IDLE; 132 | 133 | condvar_wake_all(&priv->condvar_idle); 134 | 135 | if (wh->periodic_wake > 0) { 136 | condvar_wait_time(&priv->condvar, &priv->mutex, 137 | wh->periodic_wake); 138 | if (priv->state == WORKER_IDLE) 139 | priv->state = WORKER_SIGNALED; 140 | } else { 141 | condvar_wait(&priv->condvar, &priv->mutex); 142 | } 143 | } 144 | 145 | priv->state = WORKER_STOPPED; 146 | 147 | mutex_unlock(&priv->mutex); 148 | 149 | return NULL; 150 | } 151 | 152 | void worker_free(struct worker_handle *wh) 153 | { 154 | struct worker_priv *priv = wh->priv; 155 | 156 | if (wh->priv != NULL) { 157 | worker_join(wh); 158 | 159 | thread_free(&priv->thread); 160 | mutex_free(&priv->mutex); 161 | condvar_free(&priv->condvar_idle); 162 | condvar_free(&priv->condvar); 163 | 164 | free(wh->priv); 165 | wh->priv = NULL; 166 | } 167 | } 168 | 169 | int worker_init(struct worker_handle *wh) 170 | { 171 | struct worker_priv *priv = wh->priv; 172 | int ret; 173 | 174 | if (priv == NULL) { 175 | priv = calloc(1, sizeof(*priv)); 176 | if (priv == NULL) 177 | return -ENOMEM; 178 | 179 | wh->priv = priv; 180 | } 181 | 182 | ret = condvar_init(&priv->condvar); 183 | if (ret < 0) 184 | goto worker_init_exit; 185 | 186 | ret = condvar_init(&priv->condvar_idle); 187 | if (ret < 0) 188 | goto worker_init_exit; 189 | 190 | ret = mutex_init(&priv->mutex); 191 | if (ret < 0) 192 | goto worker_init_exit; 193 | 194 | priv->thread.func_ctx = wh; 195 | priv->thread.func_ptr = worker_func; 196 | priv->thread.stack_size = wh->stack_size; 197 | ret = thread_init(&priv->thread); 198 | if (ret < 0) 199 | goto worker_init_exit; 200 | 201 | return 0; 202 | 203 | worker_init_exit: 204 | mutex_free(&priv->mutex); 205 | 206 | condvar_free(&priv->condvar_idle); 207 | condvar_free(&priv->condvar); 208 | 209 | free(wh->priv); 210 | wh->priv = NULL; 211 | 212 | return ret; 213 | } 214 | 215 | int worker_is_idle(struct worker_handle *wh) 216 | { 217 | struct worker_priv *priv = wh->priv; 218 | int ret = 0; 219 | 220 | mutex_lock_shared(&priv->mutex); 221 | 222 | if (priv->state >= WORKER_IDLE) 223 | ret = 1; 224 | 225 | mutex_unlock_shared(&priv->mutex); 226 | 227 | return ret; 228 | } 229 | 230 | int worker_join(struct worker_handle *wh) 231 | { 232 | struct worker_priv *priv = wh->priv; 233 | 234 | mutex_lock(&priv->mutex); 235 | 236 | if (priv->state > WORKER_STOPPING_AFTER_WORK) { 237 | if (priv->state == WORKER_IDLE) 238 | condvar_wake_all(&priv->condvar); 239 | 240 | if (priv->state == WORKER_SIGNALED) 241 | priv->state = WORKER_STOPPING_AFTER_WORK; 242 | else 243 | priv->state = WORKER_STOPPING; 244 | } 245 | 246 | condvar_wake_all(&priv->condvar_idle); 247 | 248 | mutex_unlock(&priv->mutex); 249 | 250 | return thread_join(&priv->thread); 251 | } 252 | 253 | int worker_start(struct worker_handle *wh) 254 | { 255 | struct worker_priv *priv = wh->priv; 256 | int ret = 0; 257 | 258 | mutex_lock(&priv->mutex); 259 | 260 | if (priv->state <= WORKER_STOPPED) { 261 | priv->state = WORKER_STARTING; 262 | ret = thread_start(&priv->thread); 263 | } 264 | 265 | mutex_unlock(&priv->mutex); 266 | 267 | return ret; 268 | } 269 | 270 | int worker_wait_idle(struct worker_handle *wh) 271 | { 272 | struct worker_priv *priv = wh->priv; 273 | int ret = 0; 274 | 275 | mutex_lock(&priv->mutex); 276 | 277 | while (priv->state < WORKER_IDLE) { 278 | if (priv->state < WORKER_BUSY) { 279 | ret = -EINVAL; 280 | break; 281 | } 282 | 283 | condvar_wait(&priv->condvar_idle, &priv->mutex); 284 | } 285 | 286 | mutex_unlock(&priv->mutex); 287 | 288 | return ret; 289 | } 290 | 291 | int worker_wake(struct worker_handle *wh) 292 | { 293 | struct worker_priv *priv = wh->priv; 294 | int ret = -EINVAL; 295 | 296 | mutex_lock(&priv->mutex); 297 | 298 | if (priv->state > WORKER_STOPPING_AFTER_WORK) { 299 | if (priv->state == WORKER_IDLE) 300 | condvar_wake_all(&priv->condvar); 301 | priv->state = WORKER_SIGNALED; 302 | ret = 0; 303 | } 304 | 305 | mutex_unlock(&priv->mutex); 306 | 307 | return ret; 308 | } 309 | -------------------------------------------------------------------------------- /include/conn.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file conn.h 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief Internal API for network connections 45 | */ 46 | 47 | #ifndef CONN_H_ 48 | #define CONN_H_ 49 | 50 | #include 51 | 52 | #ifndef _WIN32 53 | # include 54 | #endif 55 | 56 | /*! 57 | * @brief Supported connection protocols 58 | */ 59 | enum CONN_TYPE { 60 | /*! Transmission Control Protocol */ 61 | CONN_TYPE_TCP, 62 | 63 | /*! User Datagram Protocol */ 64 | CONN_TYPE_UDP 65 | }; 66 | 67 | /*! 68 | * @brief Represents an instance of a network connection 69 | * 70 | * This struct should be initialized to zero before being used. The private data 71 | * should be initialized using the ::conn_init function, and subsequently 72 | * freed by ::conn_free when the network connection is no longer needed. 73 | */ 74 | struct conn_handle { 75 | /*! Private data - used internally by conn functions */ 76 | void *priv; 77 | 78 | /*! Local network interface to bind to, or NULL for all */ 79 | const char *source_addr; 80 | 81 | /*! Local socket port to bind to, or NULL for any */ 82 | const char *source_port; 83 | 84 | /*! Protocol to use for this connection */ 85 | enum CONN_TYPE type; 86 | }; 87 | 88 | /*! 89 | * @brief Blocks until a connection is made to the given network connection 90 | * 91 | * @param[in,out] conn Target network connection instance 92 | * @param[in,out] accepted Network connection instance for newly accepted client 93 | * 94 | * @returns 0 on success, negative ERRNO value on failure 95 | */ 96 | int conn_accept(struct conn_handle *conn, struct conn_handle *accepted); 97 | 98 | /*! 99 | * @brief Closes the target connection with the client 100 | * 101 | * @param[in,out] conn Target network connection instance 102 | */ 103 | void conn_close(struct conn_handle *conn); 104 | 105 | /*! 106 | * @brief Opens a connection to a remote socket 107 | * 108 | * @param[in,out] conn Target network connection instance 109 | * @param[in] addr Address of listening network host 110 | * @param[in] port Socket port on listening network host 111 | * 112 | * @returns 0 on success, negative ERRNO value on failure 113 | */ 114 | int conn_connect(struct conn_handle *conn, const char *addr, const char *port); 115 | 116 | /*! 117 | * @brief Drops any active connections but doesn't close the connection 118 | * 119 | * @param[in,out] conn Target network connection instance 120 | */ 121 | void conn_drop(struct conn_handle *conn); 122 | 123 | /*! 124 | * @brief Frees data allocated by ::conn_init 125 | * 126 | * @param[in,out] conn Target network connection instance 127 | */ 128 | void conn_free(struct conn_handle *conn); 129 | 130 | /*! 131 | * @brief Initializes the private data in a ::conn_handle 132 | * 133 | * @param[in,out] conn Target network connection instance 134 | * 135 | * @returns 0 on success, negative ERRNO value on failure 136 | */ 137 | int conn_init(struct conn_handle *conn); 138 | 139 | /*! 140 | * @brief Determine if the connection is currently in use 141 | * 142 | * @param[in] conn Target network connection instance 143 | * 144 | * @returns 1 if in use, 0 if idle 145 | */ 146 | int conn_in_use(struct conn_handle *conn); 147 | 148 | /*! 149 | * @brief Blocking call to listen for incoming connections from clients 150 | * 151 | * @param[in,out] conn Target network connection instance 152 | * 153 | * @returns 0 on success, negative ERRNO value on failure 154 | */ 155 | int conn_listen(struct conn_handle *conn); 156 | 157 | /*! 158 | * @brief Convert a port number to an ASCII string 159 | * 160 | * @param[in] port The port number to convert 161 | * @param[out] result Resulting ASCII string 162 | */ 163 | void conn_port_to_str(uint16_t port, char result[6]); 164 | 165 | /*! 166 | * @brief Copies data which has been transferred to the connection 167 | * 168 | * @param[in] conn Target network connection instance 169 | * @param[out] buff Buffer to copy received into 170 | * @param[in] buff_len Number of bytes of data to expect 171 | * 172 | * @returns 0 on success, negative ERRNO value on failure 173 | */ 174 | int conn_recv(struct conn_handle *conn, uint8_t *buff, size_t buff_len); 175 | 176 | /*! 177 | * @brief Like ::conn_recv, but for any client and any amount of data 178 | * 179 | * @param[in] conn Target network connection instance 180 | * @param[out] buff Buffer to copy received into 181 | * @param[in] buff_len Maximum number of bytes of data to read 182 | * @param[out] addr Remote address of sending client, or NULL if unwanted 183 | * @param[out] port Remote port on sending client, or NULL if unwanted 184 | * 185 | * @returns Number of bytes copied on success, negative ERRNO value on failure 186 | */ 187 | int conn_recv_any(struct conn_handle *conn, uint8_t *buff, size_t buff_len, 188 | uint32_t *addr, uint16_t *port); 189 | 190 | /*! 191 | * @brief Send data to the connected client 192 | * 193 | * @param[in] conn Target network connection instance 194 | * @param[in] buff Buffer containing data to be sent 195 | * @param[in] buff_len Number of bytes in buff to send 196 | * 197 | * @returns 0 on success, negative ERRNO value on failure 198 | */ 199 | int conn_send(struct conn_handle *conn, const uint8_t *buff, size_t buff_len); 200 | 201 | /*! 202 | * @brief Like ::conn_send, but to a specified, unconnected client 203 | * 204 | * @param[in] conn Target network connection instance 205 | * @param[in] buff Buffer containing data to be sent 206 | * @param[in] buff_len Number of bytes in buff to send 207 | * @param[in] addr Remote address of listening client 208 | * @param[in] port Remote port on listening client 209 | * 210 | * @returns 0 on success, negative ERRNO value on failure 211 | */ 212 | int conn_send_to(struct conn_handle *conn, const uint8_t *buff, 213 | size_t buff_len, uint32_t addr, uint16_t port); 214 | 215 | /*! 216 | * @brief Set the receive timeout for a connection 217 | * 218 | * @param[in] conn Target network connection instance 219 | * @param[in,out] msec Duration to wait before returning from ::conn_recv 220 | * 221 | * @returns 0 on success, negative ERRNO value on failure 222 | * 223 | * Note that due to platform-specific behaviors, the connection should be 224 | * closed when a timeout occurs. Setting the timeout to 0 disables the 225 | * timeout, which is the default behavior when a connection is created. 226 | */ 227 | int conn_set_timeout(struct conn_handle *conn, uint32_t msec); 228 | 229 | /*! 230 | * @brief Stops socket operations but does not close the socket 231 | * 232 | * @param[in,out] conn Target network connection instance 233 | */ 234 | void conn_shutdown(struct conn_handle *conn); 235 | 236 | /*! 237 | * @brief Prints the remote address for the connection to the given ASCII string 238 | * 239 | * @param[in] conn Target network connection instance 240 | * @param[out] dest Destination ASCII string 241 | */ 242 | void conn_get_remote_addr(const struct conn_handle *conn, char dest[54]); 243 | 244 | #endif /* CONN_H_ */ 245 | -------------------------------------------------------------------------------- /test/test_regex.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file test_regex.c 3 | * 4 | * @copyright 5 | * Copyright © 2016, Scott K Logan 6 | * 7 | * @copyright 8 | * All rights reserved. 9 | * 10 | * @copyright 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * @copyright 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. Neither the name of the copyright holder nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * @copyright 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | * POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * @copyright 40 | * EchoLink® is a registered trademark of Synergenics, LLC 41 | * 42 | * @author Scott K Logan <logans@cottsay.net> 43 | * 44 | * @brief tests related to regular expressions 45 | */ 46 | 47 | #include 48 | #include 49 | #include 50 | 51 | #include "regex.h" 52 | 53 | /*! 54 | * @brief Perform a regular expression matching test 55 | * 56 | * @param[in] pattern Regular expression pattern 57 | * @param[in] subject String to match pattern against 58 | * @param[in] expected The expected return value of match 59 | * 60 | * @returns 0 on success, negative ERRNO value on failure 61 | */ 62 | static int assert_match(const char * const pattern, const char * const subject, 63 | int expected); 64 | 65 | /*! 66 | * @brief Main entry point for MD5 tests 67 | * 68 | * @returns 0 on success, non-zero value on failure 69 | */ 70 | int main(void); 71 | 72 | /*! 73 | * @brief Test regular expression catch-all case 74 | * 75 | * @returns 0 on success, negative ERRNO value on failure 76 | * 77 | * @test Test regular expression catch-all case 78 | */ 79 | static int test_regex_catchall(void); 80 | 81 | /*! 82 | * @brief Test regular expression catch-all case with empty string 83 | * 84 | * @returns 0 on success, negative ERRNO value on failure 85 | * 86 | * @test Test regular expression catch-all case with empty string 87 | */ 88 | static int test_regex_catchall_empty(void); 89 | 90 | /*! 91 | * @brief Test regular expression exact match case 92 | * 93 | * @returns 0 on success, negative ERRNO value on failure 94 | * 95 | * @test Test regular expression exact match case 96 | */ 97 | static int test_regex_exact_match(void); 98 | 99 | /*! 100 | * @brief Test regular expression negative match case 101 | * 102 | * @returns 0 on success, negative ERRNO value on failure 103 | * 104 | * @test Test regular expression negative match case 105 | */ 106 | static int test_regex_no_match(void); 107 | 108 | /*! 109 | * @brief Test regular expression OR matching exactly 110 | * 111 | * @returns 0 on success, negative ERRNO value on failure 112 | * 113 | * @test Test regular expression OR matching exactly 114 | */ 115 | static int test_regex_or_exact(void); 116 | 117 | /*! 118 | * @brief Test regular expression OR matching left side 119 | * 120 | * @returns 0 on success, negative ERRNO value on failure 121 | * 122 | * @test Test regular expression OR matching left side 123 | */ 124 | static int test_regex_or_first(void); 125 | 126 | /*! 127 | * @brief Test regular expression OR matching right side 128 | * 129 | * @returns 0 on success, negative ERRNO value on failure 130 | * 131 | * @test Test regular expression OR matching right side 132 | */ 133 | static int test_regex_or_second(void); 134 | 135 | /*! 136 | * @brief Test regular expression OR matching to a substring 137 | * 138 | * @returns 0 on success, negative ERRNO value on failure 139 | * 140 | * @test Test regular expression OR matching to a substring 141 | */ 142 | static int test_regex_or_substring(void); 143 | 144 | /*! 145 | * @brief Test regular expression OR matching to a superstring 146 | * 147 | * @returns 0 on success, negative ERRNO value on failure 148 | * 149 | * @test Test regular expression OR matching to a superstring 150 | */ 151 | static int test_regex_or_superstring(void); 152 | 153 | /*! 154 | * @brief Test regular expression OR matching to a superstring exactly 155 | * 156 | * @returns 0 on success, negative ERRNO value on failure 157 | * 158 | * @test Test regular expression OR matching to a superstring exactly 159 | */ 160 | static int test_regex_or_superstring_exact(void); 161 | 162 | /*! 163 | * @brief Try to perform a regular expression match 164 | * 165 | * @param[in] pattern Regular expression pattern 166 | * @param[in] subject String to match pattern against 167 | * 168 | * @returns 1 if matched, 0 if no match, negative ERRNO value on failure 169 | */ 170 | static int try_match(const char * const pattern, const char * const subject); 171 | 172 | static int assert_match(const char * const pattern, const char * const subject, 173 | int expected) 174 | { 175 | int ret = try_match(pattern, subject); 176 | 177 | if (ret != expected) { 178 | switch (ret) { 179 | case 1: 180 | fprintf(stderr, 181 | "Error: Regex '%s' was not expected to match subject '%s', but did\n", 182 | pattern, subject); 183 | ret = -EINVAL; 184 | break; 185 | case 0: 186 | fprintf(stderr, 187 | "Error: Regex '%s' was expected to match subject '%s', but did not\n", 188 | pattern, subject); 189 | ret = -EINVAL; 190 | break; 191 | default: 192 | fprintf(stderr, 193 | "Error: Failure while attempting to match regex '%s' to subject '%s' (%d): %s\n", 194 | pattern, subject, -ret, strerror(-ret)); 195 | break; 196 | } 197 | 198 | return ret; 199 | } 200 | 201 | return 0; 202 | } 203 | 204 | int main(void) 205 | { 206 | int ret = 0; 207 | 208 | ret |= test_regex_catchall(); 209 | ret |= test_regex_catchall_empty(); 210 | ret |= test_regex_exact_match(); 211 | ret |= test_regex_no_match(); 212 | ret |= test_regex_or_exact(); 213 | ret |= test_regex_or_first(); 214 | ret |= test_regex_or_second(); 215 | ret |= test_regex_or_substring(); 216 | ret |= test_regex_or_superstring(); 217 | ret |= test_regex_or_superstring_exact(); 218 | 219 | return ret; 220 | } 221 | 222 | static int test_regex_catchall(void) 223 | { 224 | return assert_match(".*", "KM0H", 1); 225 | } 226 | 227 | static int test_regex_catchall_empty(void) 228 | { 229 | return assert_match(".*", "", 1); 230 | } 231 | 232 | static int test_regex_exact_match(void) 233 | { 234 | return assert_match("KM0H", "KM0H", 1); 235 | } 236 | 237 | static int test_regex_no_match(void) 238 | { 239 | return assert_match("asdf", "KM0H", 0); 240 | } 241 | 242 | static int test_regex_or_exact(void) 243 | { 244 | return assert_match("^(KM0H|KD0JLT)$", "KM0H", 1); 245 | } 246 | 247 | static int test_regex_or_first(void) 248 | { 249 | return assert_match("KM0H|KD0JLT", "KM0H", 1); 250 | } 251 | 252 | static int test_regex_or_second(void) 253 | { 254 | return assert_match("KM0H|KD0JLT", "KD0JLT", 1); 255 | } 256 | 257 | static int test_regex_or_substring(void) 258 | { 259 | return assert_match("KM0H|KD0JLT", "K", 0); 260 | } 261 | 262 | static int test_regex_or_superstring(void) 263 | { 264 | return assert_match("KM0H|KD0JLT", "KKM0H", 1); 265 | } 266 | 267 | static int test_regex_or_superstring_exact(void) 268 | { 269 | return assert_match("^(KM0H|KD0JLT)$", "KKM0H", 0); 270 | } 271 | 272 | static int try_match(const char * const pattern, const char * const subject) 273 | { 274 | struct regex_handle re; 275 | int ret; 276 | 277 | memset(&re, 0x0, sizeof(struct regex_handle)); 278 | 279 | ret = regex_init(&re); 280 | if (ret < 0) { 281 | fprintf(stderr, "Error: Failed to initialize regex (%d): %s\n", 282 | -ret, strerror(-ret)); 283 | return ret; 284 | } 285 | 286 | ret = regex_compile(&re, pattern); 287 | if (ret < 0) { 288 | fprintf(stderr, 289 | "Error: Failed to compile regex '%s' (%d): %s\n", 290 | pattern, -ret, strerror(-ret)); 291 | goto try_match_exit; 292 | } 293 | 294 | ret = regex_is_match(&re, subject); 295 | if (ret < 0) 296 | fprintf(stderr, 297 | "Error: Failed to match '%s' against '%s' (%d): %s\n", 298 | subject, pattern, -ret, strerror(-ret)); 299 | 300 | try_match_exit: 301 | regex_free(&re); 302 | 303 | return ret; 304 | } 305 | --------------------------------------------------------------------------------