├── .clang-format ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── question.md ├── pull_request_template.md └── workflows │ ├── c-cpp.yml │ └── mingw-w64-x86_64.cmake ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── doc ├── AUTOBAHN.md ├── BUILD_WINDOWS.md ├── CODING_STYLE.md ├── FUZZING.md ├── TLS.md ├── doxygen │ ├── Doxyfile-mcss │ ├── doxy.conf │ └── templates │ │ ├── annotated.html │ │ ├── base-class-reference.html │ │ ├── base-index.html │ │ ├── base-reference.html │ │ ├── base.html │ │ ├── class.html │ │ ├── details-define.html │ │ ├── details-enum.html │ │ ├── details-func.html │ │ ├── details-typedef.html │ │ ├── details-var.html │ │ ├── dir.html │ │ ├── entry-class.html │ │ ├── entry-define.html │ │ ├── entry-dir.html │ │ ├── entry-enum.html │ │ ├── entry-file.html │ │ ├── entry-func.html │ │ ├── entry-module.html │ │ ├── entry-namespace.html │ │ ├── entry-typedef.html │ │ ├── entry-var.html │ │ ├── example.html │ │ ├── file.html │ │ ├── files.html │ │ ├── group.html │ │ ├── modules.html │ │ ├── namespace.html │ │ ├── namespaces.html │ │ ├── opensearch.xml │ │ ├── page.html │ │ ├── pages.html │ │ ├── struct.html │ │ └── union.html └── man │ └── man3 │ ├── ws_close_client.3 │ ├── ws_get_state.3 │ ├── ws_getaddress.3 │ ├── ws_getport.3 │ ├── ws_ping.3 │ ├── ws_sendframe.3 │ ├── ws_sendframe_bcast.3 │ ├── ws_sendframe_bin.3 │ ├── ws_sendframe_bin_bcast.3 │ ├── ws_sendframe_txt.3 │ ├── ws_sendframe_txt_bcast.3 │ └── ws_socket.3 ├── examples ├── CMakeLists.txt ├── README.md ├── echo │ ├── CMakeLists.txt │ ├── echo.c │ └── echo.html ├── ping │ ├── CMakeLists.txt │ └── ping.c └── vtouchpad │ ├── CMakeLists.txt │ ├── README.md │ ├── mouse_win.c │ ├── mouse_x11.c │ ├── vtouchpad.c │ ├── vtouchpad.h │ └── vtouchpad.html ├── extra └── toyws │ ├── README.md │ ├── toyws.c │ ├── toyws.h │ └── tws_test.c ├── include ├── base64.h ├── sha1.h ├── utf8.h └── ws.h ├── src ├── base64.c ├── handshake.c ├── sha1.c ├── utf8.c └── ws.c └── tests ├── CMakeLists.txt ├── Makefile ├── fuzzy ├── Makefile ├── in │ ├── ch_1b_1b_508b_close │ └── ch_1b_close ├── packets │ ├── ch_508b_close │ ├── ch_close │ ├── ff_1b_close │ ├── ff_384kB_close │ ├── ff_ping_ping_close │ ├── frames │ │ ├── close │ │ ├── ping │ │ ├── req_chrome │ │ ├── req_firefox │ │ └── req_websocat │ ├── msgs │ │ ├── msg_1byte │ │ ├── msg_384kB_cont │ │ ├── msg_508bytes │ │ └── msg_98305bytes │ ├── ws_1b_close │ ├── ws_508b_ping_close │ └── ws_98305b_close ├── run-fuzzy.sh └── ws_file.c ├── run-autobahn.sh ├── validate_output.py └── wsserver_autobahn └── fuzzingclient.json /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Microsoft 3 | AlignAfterOpenBracket: DontAlign 4 | AlignConsecutiveMacros: 'true' 5 | AlignConsecutiveAssignments: 'false' 6 | AlignEscapedNewlines: Left 7 | AlignTrailingComments: 'true' 8 | AllowAllParametersOfDeclarationOnNextLine: 'true' 9 | AllowShortCaseLabelsOnASingleLine: 'false' 10 | AllowShortIfStatementsOnASingleLine: Never 11 | AllowShortLoopsOnASingleLine: 'false' 12 | BinPackParameters: 'false' 13 | ColumnLimit: '85' 14 | IndentCaseLabels: 'true' 15 | IndentWidth: '4' 16 | Language: Cpp 17 | PointerAlignment: Right 18 | SpaceAfterCStyleCast: 'false' 19 | SpaceBeforeParens: ControlStatements 20 | SpaceInEmptyParentheses: 'false' 21 | SpacesInCStyleCastParentheses: 'false' 22 | SpacesInContainerLiterals: 'false' 23 | SpacesInParentheses: 'false' 24 | SpacesInSquareBrackets: 'false' 25 | TabWidth: '4' 26 | UseTab: ForContinuationAndIndentation 27 | 28 | ... 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Is something not working as expected? let us know... 4 | title: "[bug]" 5 | labels: possible-bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | Description 11 | ----------- 12 | A clear and concise description of what the bug is. 13 | 14 | Your environment 15 | ---------------- 16 | Describe your environment: 17 | - wsServer version (commit hash) 18 | - OS version: Windows 10, Ubuntu Linux 20.04, Slackware 15, macOS Sierra v10.12.6, FreeBSD 14.0... 19 | - If Linux: 20 | - kernel version: v4.4.38, v6.6.3... 21 | - Libc used: glibc v2.30, Musl v1.2.4... 22 | - If Windows: 23 | - Is it running under WSL? yes/no 24 | - Is it running under MinGW? yes/no 25 | - Architecture: aarch64, armv7, i386 (32-bit), x86-64... 26 | - Client: Google Chrome v118.0.5993.117, Firefox v120.0, websocat v1.6.0... 27 | - If browser: which website? 28 | - Have you tested under the echo/echo.html example? yes/no 29 | 30 | How to reproduce (if possible) 31 | ------------------------------ 32 | Steps to reproduce the behavior: 33 | 1. Go to '...' 34 | 2. Click on '....' 35 | 3. Scroll down to '....' 36 | 4. See error 37 | 38 | Additional context 39 | ------------------ 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[feature]" 5 | labels: feature 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Don't know how something works? Do you have questions about whether wsServer 4 | supports XYZ feature? If you have this or other questions, ask here =). 5 | title: "[question]" 6 | labels: question 7 | assignees: '' 8 | 9 | --- 10 | 11 | Description 12 | ----------- 13 | Describe your question here... (you can also use discussions for this, if you feel more comfortable there) 14 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 27 | 28 | Description 29 | ----------- 30 | _Describe your PR here_ 31 | 32 | Checklist 33 | --------- 34 | - [ ] I've read the notice in the PR template before submitting it 35 | - My PR is: 36 | - Trivial and: 37 | - [ ] I've created an issue (please mention the issue number) 38 | - [ ] I haven't created an issue (thats ok...) 39 | - Non-trivial: 40 | - Issue number: 41 | -------------------------------------------------------------------------------- /.github/workflows/c-cpp.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2024 Davidson Francis 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see 16 | # 17 | 18 | name: CI 19 | 20 | env: 21 | CTEST_OUTPUT_ON_FAILURE: 1 22 | TRAVIS: 1 23 | 24 | on: 25 | push: 26 | branches: [ "master" ] 27 | pull_request: 28 | branches: [ "master" ] 29 | 30 | jobs: 31 | linux: 32 | name: Linux (AMD64) Build & Test 33 | runs-on: ubuntu-latest 34 | 35 | steps: 36 | - name: Checkout 37 | uses: actions/checkout@v4 38 | - name: Build & Tests 39 | run: > 40 | mkdir build && 41 | cd build && 42 | cmake .. -DCMAKE_BUILD_TYPE=Release -DENABLE_WSSERVER_TEST=On && 43 | make -j$(nproc) && 44 | CTEST_OUTPUT_ON_FAILURE=1 ctest --verbose 45 | 46 | windows: 47 | name: Windows (via Wine) (AMD64) Build & Test 48 | runs-on: ubuntu-latest 49 | 50 | steps: 51 | - name: Checkout 52 | uses: actions/checkout@v4 53 | - name: Install toolchain 54 | run: sudo apt-get install mingw-w64 wine64 -y 55 | - name: Build & Tests 56 | run: > 57 | wget https://raw.githubusercontent.com/Theldus/wsServer/master/.github/workflows/mingw-w64-x86_64.cmake && 58 | mkdir build && 59 | cd build && 60 | cmake .. -DCMAKE_TOOLCHAIN_FILE=../mingw-w64-x86_64.cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_WSSERVER_TEST=On && 61 | make -j$(nproc) && 62 | CTEST_OUTPUT_ON_FAILURE=1 ctest --verbose 63 | 64 | macOS: 65 | name: macOS (AMD64) Build 66 | runs-on: macos-latest 67 | 68 | steps: 69 | - name: Checkout 70 | uses: actions/checkout@v4 71 | - name: Build 72 | run: > 73 | mkdir build && 74 | cd build && 75 | cmake .. -DCMAKE_BUILD_TYPE=Release && 76 | cmake --build . 77 | -------------------------------------------------------------------------------- /.github/workflows/mingw-w64-x86_64.cmake: -------------------------------------------------------------------------------- 1 | # Sample toolchain file for building for Windows from an Ubuntu Linux system. 2 | # 3 | # Typical usage: 4 | # *) install cross compiler: `sudo apt-get install mingw-w64` 5 | # *) cd build 6 | # *) cmake -DCMAKE_TOOLCHAIN_FILE=~/mingw-w64-x86_64.cmake .. 7 | 8 | set(CMAKE_SYSTEM_NAME Windows) 9 | set(TOOLCHAIN_PREFIX x86_64-w64-mingw32) 10 | 11 | # cross compilers to use for C, C++ and Fortran 12 | set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) 13 | set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++) 14 | set(CMAKE_Fortran_COMPILER ${TOOLCHAIN_PREFIX}-gfortran) 15 | set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres) 16 | 17 | # target environment on the build host system 18 | set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) 19 | 20 | # modify default behavior of FIND_XXX() commands 21 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 22 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 23 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 Davidson Francis 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see 15 | 16 | # Ignore object files, executables and the library. 17 | *.o 18 | *.a 19 | ws_file 20 | toyws_test 21 | build/ 22 | doc/doxygen/html 23 | doc/doxygen/xml 24 | tests/wsserver_autobahn/report 25 | tests/fuzzy/out/ 26 | examples/echo/echo 27 | examples/ping/ping 28 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2022 Davidson Francis 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see 15 | 16 | cmake_minimum_required(VERSION 3.5.2) 17 | 18 | project(ws C) 19 | 20 | set(CMAKE_C_STANDARD 99) 21 | 22 | include_directories(include) 23 | 24 | add_library(ws 25 | src/ws.c 26 | src/base64.c 27 | src/sha1.c 28 | src/handshake.c 29 | src/utf8.c 30 | ) 31 | 32 | target_include_directories(ws INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") 33 | 34 | if(WIN32) 35 | target_link_libraries(ws pthread ws2_32 -static) 36 | else() 37 | target_link_libraries(ws pthread) 38 | endif(WIN32) 39 | 40 | add_executable(toyws_test 41 | extra/toyws/tws_test.c 42 | extra/toyws/toyws.c) 43 | 44 | if(WIN32) 45 | target_link_libraries(toyws_test ws2_32 -static) 46 | endif(WIN32) 47 | 48 | # Examples files 49 | add_subdirectory(examples) 50 | 51 | option(ENABLE_WSSERVER_TEST "Enable wsServer testing (requires Autobahn)" OFF) 52 | if(ENABLE_WSSERVER_TEST) 53 | enable_testing() 54 | # Disable verbose output of echo 55 | target_compile_definitions(echo PRIVATE DISABLE_VERBOSE) 56 | add_subdirectory(tests) 57 | endif(ENABLE_WSSERVER_TEST) 58 | 59 | option(VALIDATE_UTF8 "Enable UTF-8 validation (default ON)" ON) 60 | if(VALIDATE_UTF8) 61 | target_compile_definitions(ws PRIVATE VALIDATE_UTF8) 62 | endif(VALIDATE_UTF8) 63 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2023 Davidson Francis 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see 15 | 16 | CC ?= gcc 17 | AR = ar 18 | LIB_WS = libws.a 19 | INCLUDE = include 20 | CFLAGS += -Wall -Wextra -O2 21 | CFLAGS += -I $(INCLUDE) -std=c99 -pedantic 22 | LDLIBS = $(LIB_WS) -pthread 23 | ARFLAGS = cru 24 | MCSS_DIR ?= /usr/bin/ 25 | MANPAGES = doc/man/man3 26 | AFL_FUZZ ?= no 27 | VERBOSE_EXAMPLES ?= yes 28 | VALIDATE_UTF8 ?= yes 29 | 30 | # Prefix 31 | ifeq ($(PREFIX),) 32 | PREFIX := /usr/local 33 | endif 34 | 35 | # Detect machine type 36 | MACHINE := $(shell uname -m) 37 | ifeq ($(MACHINE), x86_64) 38 | LIBDIR = $(PREFIX)/lib64 39 | else 40 | LIBDIR = $(PREFIX)/lib 41 | endif 42 | 43 | # Check if AFL fuzzing enabled 44 | ifeq ($(AFL_FUZZ), yes) 45 | CC = afl-gcc 46 | CFLAGS := $(filter-out -O2,$(CFLAGS)) 47 | CFLAGS += -DVERBOSE_MODE -DAFL_FUZZ -g 48 | $(info [+] AFL Fuzzing build enabled) 49 | endif 50 | 51 | # Check if UTF-8 validation is enabled 52 | ifeq ($(VALIDATE_UTF8), yes) 53 | CFLAGS += -DVALIDATE_UTF8 54 | endif 55 | 56 | # Pretty print 57 | Q := @ 58 | ifeq ($(V), 1) 59 | Q := 60 | endif 61 | 62 | # Conflicts 63 | .PHONY: all examples tests fuzzy install uninstall doc clean 64 | 65 | # Paths 66 | INCDIR = $(PREFIX)/include 67 | BINDIR = $(PREFIX)/bin 68 | MANDIR = $(PREFIX)/man 69 | PKGDIR = $(LIBDIR)/pkgconfig 70 | 71 | # Extra paths 72 | TOYWS = extra/toyws 73 | 74 | # All 75 | ifeq ($(AFL_FUZZ),no) 76 | all: Makefile libws.a examples $(TOYWS)/toyws_test 77 | else 78 | all: Makefile libws.a fuzzy 79 | endif 80 | 81 | # 82 | # Library 83 | # 84 | 85 | %.o: %.c 86 | @echo " CC $@" 87 | $(Q)$(CC) $(CFLAGS) -c -o $@ $< 88 | 89 | # Source 90 | WS_OBJ = src/base64.o \ 91 | src/handshake.o \ 92 | src/sha1.o \ 93 | src/utf8.o \ 94 | src/ws.o 95 | 96 | # Headers 97 | src/ws.o: include/ws.h include/utf8.h 98 | src/base.o: include/base64.h 99 | src/handshake.o: include/base64.h include/ws.h include/sha1.h 100 | src/sha1.o: include/sha1.h 101 | src/utf8.o: include/utf8.h 102 | 103 | # Lib 104 | $(LIB_WS): $(WS_OBJ) 105 | @echo " AR $@" 106 | $(Q)$(AR) $(ARFLAGS) $(LIB_WS) $^ 107 | 108 | # Examples 109 | examples: examples/echo/echo examples/ping/ping 110 | examples/echo/echo: examples/echo/echo.o $(LIB_WS) 111 | @echo " LINK $@" 112 | $(Q)$(CC) $(CFLAGS) $< -o $@ $(LDLIBS) 113 | examples/ping/ping: examples/ping/ping.o $(LIB_WS) 114 | @echo " LINK $@" 115 | $(Q)$(CC) $(CFLAGS) $< -o $@ $(LDLIBS) 116 | 117 | # Autobahn tests 118 | tests: examples 119 | $(MAKE) all -C tests/ VERBOSE_EXAMPLES="$(VERBOSE_EXAMPLES)" 120 | tests_check: 121 | $(MAKE) check_results -C tests/ 122 | 123 | # Fuzzing tests 124 | fuzzy: libws.a 125 | $(MAKE) -C tests/fuzzy 126 | 127 | # ToyWS client 128 | $(TOYWS)/toyws_test: $(TOYWS)/tws_test.o $(TOYWS)/toyws.o 129 | @echo " LINK $@" 130 | $(Q)$(CC) $(CFLAGS) $^ -o $@ 131 | 132 | # Install rules 133 | install: libws.a wsserver.pc 134 | @echo " INSTALL $@" 135 | @#Library 136 | install -d $(DESTDIR)$(LIBDIR) 137 | install -m 644 $(LIB_WS) $(DESTDIR)$(LIBDIR) 138 | @#Headers 139 | install -d $(DESTDIR)$(INCDIR)/wsserver 140 | install -m 644 $(INCLUDE)/*.h $(DESTDIR)$(INCDIR)/wsserver 141 | @#Manpages 142 | install -d $(DESTDIR)$(MANDIR)/man3 143 | install -m 0644 $(MANPAGES)/*.3 $(DESTDIR)$(MANDIR)/man3/ 144 | 145 | # Uninstall rules 146 | uninstall: 147 | @echo " UNINSTALL $@" 148 | rm -f $(DESTDIR)$(LIBDIR)/$(LIB_WS) 149 | rm -rf $(DESTDIR)$(INCDIR)/wsserver 150 | rm -f $(DESTDIR)$(MANDIR)/man3/ws_getaddress.3 151 | rm -f $(DESTDIR)$(MANDIR)/man3/ws_getport.3 152 | rm -f $(DESTDIR)$(MANDIR)/man3/ws_sendframe.3 153 | rm -f $(DESTDIR)$(MANDIR)/man3/ws_sendframe_bcast.3 154 | rm -f $(DESTDIR)$(MANDIR)/man3/ws_sendframe_bin.3 155 | rm -f $(DESTDIR)$(MANDIR)/man3/ws_sendframe_bin_bcast.3 156 | rm -f $(DESTDIR)$(MANDIR)/man3/ws_sendframe_txt.3 157 | rm -f $(DESTDIR)$(MANDIR)/man3/ws_sendframe_txt_bcast.3 158 | rm -f $(DESTDIR)$(MANDIR)/man3/ws_socket.3 159 | rm -f $(DESTDIR)$(MANDIR)/man3/ws_close_client.3 160 | rm -f $(DESTDIR)$(MANDIR)/man3/ws_get_state.3 161 | rm -f $(DESTDIR)$(PKGDIR)/wsserver.pc 162 | 163 | # Generate wsserver.pc 164 | wsserver.pc: 165 | @install -d $(DESTDIR)$(PKGDIR) 166 | @echo 'prefix='$(PREFIX) > $(DESTDIR)$(PKGDIR)/wsserver.pc 167 | @echo 'libdir='$(LIBDIR) >> $(DESTDIR)$(PKGDIR)/wsserver.pc 168 | @echo 'includedir=$${prefix}/include' >> $(DESTDIR)$(PKGDIR)/wsserver.pc 169 | @echo 'Name: wsServer' >> $(DESTDIR)$(PKGDIR)/wsserver.pc 170 | @echo 'Description: Tiny WebSocket Server Library' >> $(DESTDIR)$(PKGDIR)/wsserver.pc 171 | @echo 'Version: 1.0' >> $(DESTDIR)$(PKGDIR)/wsserver.pc 172 | @echo 'Libs: -L$${libdir} -lws -pthread' >> $(DESTDIR)$(PKGDIR)/wsserver.pc 173 | @echo 'Libs.private:' >> $(DESTDIR)$(PKGDIR)/wsserver.pc 174 | @echo 'Cflags: -I$${includedir}/wsserver' >> $(DESTDIR)$(PKGDIR)/wsserver.pc 175 | 176 | # Documentation, requires Doxygen and m.css 177 | # -> https://mcss.mosra.cz/documentation/doxygen/ 178 | # 179 | # If for some reason m.css usage is not possible, 180 | # change the following lines in doxy.conf to: 181 | # GENERATE_HTML = no -> GENERATE_HTML = yes 182 | # GENERATE_XML = yes -> GENERATE_XML = no 183 | # 184 | doc: 185 | @echo "Generating docs..." 186 | @doxygen doc/doxygen/doxy.conf 187 | @$(MCSS_DIR)/doxygen.py doc/doxygen/Doxyfile-mcss --no-doxygen\ 188 | --templates doc/doxygen/templates/ 189 | 190 | # Clean 191 | clean: 192 | @rm -f $(WS_OBJ) 193 | @rm -f $(LIB_WS) 194 | @rm -f $(TOYWS)/toyws.o $(TOYWS)/tws_test.o $(TOYWS)toyws_test 195 | @rm -f examples/echo/{echo,echo.o} 196 | @rm -f examples/ping/{ping,ping.o} 197 | @$(MAKE) clean -C tests/ 198 | @$(MAKE) clean -C tests/fuzzy 199 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # wsServer 3 | [![License: GPL v3](https://img.shields.io/badge/license-GPLv3-blue)](https://opensource.org/licenses/GPL-3.0) 4 | [![Build Status for Windows, Linux, and macOS](https://github.com/Theldus/wsServer/actions/workflows/c-cpp.yml/badge.svg)](https://github.com/Theldus/wsServer/actions/workflows/c-cpp.yml) 5 | 6 | wsServer - a very tiny WebSocket server library written in C 7 | 8 | ## Library 9 | 10 | wsServer is a tiny, lightweight WebSocket server library written in C that intends 11 | to be easy to use, fast, hackable, and compliant to the 12 | [RFC 6455](https://tools.ietf.org/html/rfc6455). 13 | 14 | The main features are: 15 | - Send/Receive Text and Binary messages 16 | - PING/PONG frames 17 | - Opening/Closing handshakes 18 | - Event based (onmessage, onopen, onclose) 19 | - Portability: Works fine on Windows, Linux (Android included), macOS and FreeBSD 20 | 21 | See Autobahn [report](https://theldus.github.io/wsServer/autobahn) and the 22 | [docs](doc/AUTOBAHN.md) for an 'in-depth' analysis. 23 | 24 | ## Building 25 | 26 | wsServer only requires a C99-compatible compiler (such as GCC, Clang, TCC and others) and 27 | no external libraries. 28 | 29 | ### Make 30 | The preferred way to build wsServer on Linux environments: 31 | ```bash 32 | git clone https://github.com/Theldus/wsServer 33 | cd wsServer/ 34 | make 35 | 36 | # Optionally, a user can also install wsServer into the system, 37 | # either on default paths or by providing PREFIX or DESTDIR env 38 | # vars to the Makefile. 39 | 40 | make install # Or make install DESTDIR=/my/folder/ 41 | ``` 42 | 43 | ### CMake 44 | CMake enables the user to easily build wsServer in others environments other than Linux 45 | and also allows the use of an IDE to build the project automatically. If that's 46 | your case: 47 | ```bash 48 | git clone https://github.com/Theldus/wsServer 49 | cd wsServer/ 50 | mkdir build && cd build/ 51 | cmake .. 52 | make 53 | ./examples/echo/echo # Waiting for incoming connections... 54 | ``` 55 | 56 | ### Windows support 57 | Windows has native support via MinGW, toolchain setup and build steps are detailed 58 | [here](https://github.com/Theldus/wsServer/blob/master/doc/BUILD_WINDOWS.md). 59 | 60 | ## Keeping It Simple! 61 | 62 | wsServer simplifies socket management by allowing you to focus on only three different 63 | sorts of events: 64 | 65 | ```c 66 | /* New client. */ 67 | void onopen(ws_cli_conn_t client); 68 | 69 | /* Client disconnected. */ 70 | void onclose(ws_cli_conn_t client); 71 | 72 | /* Client sent a text message. */ 73 | void onmessage(ws_cli_conn_t client, const unsigned char *msg, 74 | uint64_t size, int type); 75 | ``` 76 | 77 | This is the only thing you need to worry about. You don’t have to think about return 78 | values in socket, accepting connections, or anything else. As a bonus, each client is 79 | handled in a separate thread, so there is no need to worry about that either. 80 | 81 | ### A complete example 82 | 83 | More examples, including their respective html files, can be found in examples/ 84 | folder, ;-). 85 | 86 | ```c 87 | #include 88 | #include 89 | #include 90 | #include 91 | 92 | /** 93 | * @brief This function is called whenever a new connection is opened. 94 | * @param client Client connection. 95 | */ 96 | void onopen(ws_cli_conn_t client) 97 | { 98 | char *cli; 99 | cli = ws_getaddress(client); 100 | printf("Connection opened, addr: %s\n", cli); 101 | } 102 | 103 | /** 104 | * @brief This function is called whenever a connection is closed. 105 | * @param client Client connection. 106 | */ 107 | void onclose(ws_cli_conn_t client) 108 | { 109 | char *cli; 110 | cli = ws_getaddress(client); 111 | printf("Connection closed, addr: %s\n", cli); 112 | } 113 | 114 | /** 115 | * @brief Message events goes here. 116 | * @param client Client connection. 117 | * @param msg Message content. 118 | * @param size Message size. 119 | * @param type Message type. 120 | */ 121 | void onmessage(ws_cli_conn_t client, 122 | const unsigned char *msg, uint64_t size, int type) 123 | { 124 | char *cli; 125 | cli = ws_getaddress(client); 126 | printf("I receive a message: %s (%zu), from: %s\n", msg, 127 | size, cli); 128 | 129 | sleep(2); 130 | ws_sendframe_txt(client, "hello"); 131 | sleep(2); 132 | ws_sendframe_txt(client, "world"); 133 | } 134 | 135 | int main(void) 136 | { 137 | /* 138 | * Main loop, this function never* returns. 139 | * 140 | * *If the .thread_loop is != 0, a new thread is created 141 | * to handle new connections and ws_socket() becomes 142 | * non-blocking. 143 | */ 144 | ws_socket(&(struct ws_server){ 145 | /* 146 | * Bind host, such as: 147 | * localhost -> localhost/127.0.0.1 148 | * 0.0.0.0 -> global IPv4 149 | * :: -> global IPv4+IPv6 (Dual stack) 150 | */ 151 | .host = "localhost", 152 | .port = 8080, 153 | .thread_loop = 0, 154 | .timeout_ms = 1000, 155 | .evs.onopen = &onopen, 156 | .evs.onclose = &onclose, 157 | .evs.onmessage = &onmessage 158 | }); 159 | 160 | return (0); 161 | } 162 | ``` 163 | 164 | the example above can be built with: `make examples`. 165 | 166 | ## WebSocket client: ToyWS 167 | Inside `extra/toyws` there is a companion project called ToyWS. ToyWS is a very 168 | simple & dumb WebSocket client made exclusively to work with wsServer. Extremely 169 | limited, its usage is highly discouraged with other servers other than wsServer 170 | and is only meant to be used in conjunction with wsServer. 171 | 172 | This mini-project only serves as an aid to wsServer and frees the user from 173 | using additional projects to use wsServer in its entirety. 174 | 175 | More info at: [extra/toyws/README.md](extra/toyws/README.md) 176 | 177 | ## SSL/TLS Support 178 | wsServer does not currently support encryption. However, it is possible to use it 179 | in conjunction with [Stunnel](https://www.stunnel.org/), a proxy that adds TLS 180 | support to existing projects. Just follow [these](doc/TLS.md) four easy steps 181 | to get TLS support on wsServer. 182 | 183 | ## Contributing 184 | wsServer is always open to the community and willing to accept contributions, 185 | whether with issues, documentation, testing, new features, bugfixes, typos... 186 | welcome aboard. Make sure to read the [coding-style](doc/CODING_STYLE.md) 187 | guidelines before sending a PR. 188 | 189 | Was the project helpful to you? Have something to say about it? Leave your 190 | comments [here](https://github.com/Theldus/wsServer/discussions/30). 191 | 192 | ## License and Authors 193 | wsServer is licensed under GPLv3 License. Written by Davidson Francis and 194 | [others](https://github.com/Theldus/wsServer/graphs/contributors) 195 | contributors. 196 | -------------------------------------------------------------------------------- /doc/AUTOBAHN.md: -------------------------------------------------------------------------------- 1 | # Autobahn and standard support 2 | Although wsServer strives to be as simple as possible, the library does intend to used 3 | in production and relies on two (but not limited to) tools for that: [AFL](FUZZING.md) 4 | and Autobahn WebSocket Testsuite. The former ensures that wsServer is stable enough 5 | to be run even under unexpected conditions, such as when under attack. The latter 6 | is discussed below. 7 | 8 | [Autobahn|Testsuite](https://github.com/crossbario/autobahn-testsuite) is a third-party 9 | tool that performs a series of automated tests that verify the correctness of client and 10 | server websocket implementations in conformance to the specification. With more than 500 11 | test cases, Autobahn extensively tests client and server implementations and also evaluates 12 | its performance. 13 | 14 | ## Run tests 15 | Testing requires pre-installation of 16 | [Autobahn|Testsuite](https://github.com/crossbario/autobahn-testsuite). Alternatively, it is 17 | possible to use the Docker image used in the CI tests, in this case, export the environment 18 | variable 'TRAVIS', as `export TRAVIS=true`. 19 | 20 | After that, tests can be invoked via Makefile or CMake: 21 | ### Makefile 22 | ```bash 23 | # Ensure project is in clean state 24 | $ make clean 25 | # Build and execute tests 26 | $ make tests 27 | ``` 28 | ### CMake 29 | ```bash 30 | $ mkdir build && cd build/ 31 | # Configure project and enable tests build 32 | $ cmake .. \ 33 | -DCMAKE_BUILD_TYPE=Release \ 34 | -DENABLE_WSSERVER_TEST=On 35 | # Build project 36 | $ make -j4 37 | # Execute tests 38 | $ make test 39 | ``` 40 | 41 | ## Tests results 42 | From the [tests](https://theldus.github.io/wsServer/autobahn), it can be seen that 43 | wsServer passes all Autobahn|Testsuite tests. The only tests that are not run (12.* 44 | and 13.*) concern WebSocket Compression, which is defined as an extension of the 45 | websocket protocol and defined in 46 | [RFC 7692](https://datatracker.ietf.org/doc/html/rfc7692). Compression does not 47 | belong to [RFC 6455](https://tools.ietf.org/html/rfc6455). 48 | 49 | Therefore, I believe it is safe to say that wsServer is RFC 6455 compliant and should 50 | behave correctly in different scenarios. Any unexpected behavior regarding communication 51 | with the client is considered an error, and an issue must be reported. 52 | -------------------------------------------------------------------------------- /doc/BUILD_WINDOWS.md: -------------------------------------------------------------------------------- 1 | # Building on Windows 2 | 3 | ## Introduction 4 | Unlike Linux, Windows does not have by default a package manager or native build 5 | tools, which makes the process a little more laborious, but the sections below 6 | clarify in detail what must be done to natively build the library. 7 | 8 | ## MSYS2 Setup 9 | 10 | MSYS2 is a set of tools and libraries that provide an easy-to-use development 11 | environment and is, in my opinion, the most straightforward way to use C in 12 | Windows environments. 13 | 14 | The [home page](https://www.msys2.org/) already illustrates the basic 15 | step-by-step for the initial setup of MSYS2 but below is a very brief 16 | description of what to do: 17 | 18 | (note: if you already have MinGW and CMake properly installed, you can skip this 19 | section entirely) 20 | 21 | ### 1) Download and install MSYS2 22 | From the homepage (informed above) download the latest version available, or if 23 | you prefer, use the same version tested here: 24 | [msys2-x86_64-20210604.exe](https://github.com/msys2/msys2-installer/releases/download/2021-06-04/msys2-x86_64-20210604.exe). 25 | 26 | ### 2) Update packages 27 | MSYS2 uses Pacman, a package manager, that is, a program responsible for 28 | managing programs, libraries, and their dependencies installed on a machine. 29 | Very well known among Arch Linux users, it is very useful to have it on MSYS2. 30 | 31 | 1) First, it is necessary to update the package database and base packages. If 32 | it hasn't already opened, run 'MSYS2 MSYS' from the Start menu and run 33 | `pacman -Syu`. 34 | 35 | 2) To proceed with the update, the terminal needs to be closed and opened again. 36 | After that, continue the update with: `pacman -Su`. 37 | 38 | ### 3) Install packages 39 | In addition to the base packages, some packages need to be installed to have a 40 | minimal development environment: 41 | 42 | 1) Basic packages and GCC toolchain: 43 | `pacman -S --needed base-devel mingw-w64-x86_64-toolchain`. 44 | 2) CMake and Git: `pacman -S mingw-w64-x86_64-cmake git`. 45 | 46 | ## wsServer build 47 | With MSYS2 up and running, you have everything you need to download, compile and 48 | run the wsServer and its examples. 49 | 50 | To do this, run 'MSYS2 MinGW 64bit' from the Start menu and: 51 | ```bash 52 | $ git clone https://github.com/Theldus/wsServer.git 53 | $ cd wsServer 54 | $ mkdir build && cd build/ 55 | $ cmake .. -DCMAKE_BUILD_TYPE=Release -G "MinGW Makefiles" 56 | $ mingw32-make -j4 57 | ``` 58 | And that's it, the static library (libws.a) and the examples files are located in the 59 | build/examples folder. 60 | 61 | The `echo` example can be run with: `./examples/echo/echo.exe`, and the test webpage can be 62 | found at `wsServer/examples/echo`. 63 | -------------------------------------------------------------------------------- /doc/CODING_STYLE.md: -------------------------------------------------------------------------------- 1 | # wsServer Coding Style 2 | 3 | wsServer is a simple project, and therefore I don't believe there is so much 4 | interest in it. However, as I have received a few contributions over the years, 5 | it may be interesting to write something about it. 6 | 7 | (much of what is here is (heavily-)based on 8 | [Nanvix](https://github.com/nanvix/documentation/blob/master/6-contrib/3-coding-style.md)'s 9 | and 10 | [Linux](https://www.kernel.org/doc/html/v4.10/process/coding-style.html)'s 11 | coding style.) 12 | 13 | ## Indentation 14 | - _Always_ use **tab** characters for indentation. 15 | - Tab width: 4 characters. 16 | - Indent case labels in switch statements, such as: 17 | ```c 18 | switch (var) 19 | { 20 | case bar: 21 | something; 22 | break; 23 | case beef: 24 | something 25 | break; 26 | default: 27 | break; 28 | } 29 | ``` 30 | - Do not put multiples statements nor multiples assignments in a single line. 31 | 32 | ## Long lines length 33 | - Even though Mr. Torvalds has [said](https://www.theregister.com/2020/06/01/linux_5_7/) 34 | that nowadays it is ok to use large lines, I'll keep to 80 columns with five (85) 35 | of tolerance. However, use common sense here. 36 | 37 | - While breaking long lines, indent the next line at one level. 38 | 39 | ## Placing Braces and Spaces 40 | - Allman style: whether functions or control statements, place the braces always 41 | in the next line. Statements within the braces are indented at one level. 42 | 43 | Ex: 44 | ```c 45 | void foo(void) 46 | { 47 | if (something) 48 | { 49 | bar(); 50 | baz(); 51 | } 52 | else 53 | deadbeef(); 54 | 55 | something_else(); /* put always a blank line between a control block 56 | without braces and the next statement. */ 57 | } 58 | ``` 59 | - If one line is enough (like function call or variable assignment), do not put 60 | braces. Keep in mind the readability: put a blank line between statements. 61 | 62 | ## Spaces 63 | - Use one space after these keywords: `if, switch, case, for, do, while`. 64 | - Use one space around (on each side of) most binary and ternary operators: 65 | `= + - < > * / % | & ^ <= >= == != ? :`. 66 | - But no space after unary operators: `& * + - ~ ! sizeof`. 67 | - No space after/before the postfix/prefix increment & decrement unary operators: 68 | `++ --`. 69 | 70 | ## Variables and Naming 71 | - Do not use mixed-cases to name your variables. Always use lower-case for 72 | variables and functions name. Underscores are acceptable to give a more descriptive 73 | name, e.g., `int next_frame(...)`. 74 | - Always use uppercase for macros. 75 | - Do not use _typedef_ s unless for totally opaque objects. The reasoning is 76 | simple: it _hides_ the original meaning of the variable. Is it a union, struct, 77 | an integer?. 78 | - (Try to) do not mix variable declaration with your code. Declare your variables 79 | at the very beginning of the scope of that variable. 80 | 81 | ## Commenting/Documenting 82 | - Use always `/* c89 style. */` in your code, even for a single line. 83 | - Use Doxygen syntax to document functions and structures, even for the internal 84 | API. 85 | 86 | --- 87 | 88 | ## Using clang-format 89 | `clang-format` is a command-line tool, part of the LLVM project that, for a 90 | pre-defined set of rules or a given file, automatically formats source code. 91 | wsServer comes with a .clang-format file in the root directory that tries 92 | to embrace most of these rules above. 93 | 94 | Please note that the .clang-format file does not fully cover everything described 95 | here and even in cases that do, it is always important to carefully evaluate the 96 | output generated by it, since sometimes it may be far from ideal, use common 97 | sense here. 98 | 99 | My suggestion would be something like: 100 | - Commit what you need to commit (**locally**). 101 | - Apply clang-format over the file: `clang-format -style=file -i src/file.c`. 102 | - Manually check changes on the file. 103 | - Make a commit amend if applicable. 104 | 105 | Similarly, instead of performing an amend, you can also generate a newly formatted 106 | file and then perform a diff of the original with the new one: 107 | ```bash 108 | clang-format -style=file file.c > newfile.c 109 | diff -u file.c newfile.c > diffs.patch # see this file 110 | ``` 111 | Pick what you think is best. 112 | -------------------------------------------------------------------------------- /doc/FUZZING.md: -------------------------------------------------------------------------------- 1 | # Fuzzing wsServer 2 | wsServer intends to be robust enough to be used safely in production. Thus, 3 | the project supports fuzzing through the `ws_file` routine, which reads a file 4 | containing previously captured packets from the network. This routine allows 5 | wsServer to be tested for common cases and expected to work and permits it to 6 | be used on fuzzers, such as the AFL, supported here. 7 | 8 | ## 1) Installing/building American Fuzzy Lop 9 | While not the focus here, building AFL should not be an issue, so the 10 | following brief instructions should be sufficient: 11 | 12 | ```bash 13 | # Clone the repository 14 | git clone https://github.com/google/AFL 15 | 16 | # Build 17 | cd AFL/ 18 | make 19 | 20 | # Set env vars 21 | export PATH=$PATH:$(pwd) 22 | export AFL_PATH=$(pwd) 23 | 24 | # Add env vars into your ~/.bashrc 25 | echo "export PATH=\$PATH:$(pwd)/" >> ~/.bashrc 26 | echo "AFL_PATH=$(pwd)" >> ~/.bashrc 27 | ``` 28 | If anything fails, please check if you have the common build tools on your 29 | system (such as `gcc`, `make`, etc.) and read the official or specific 30 | instructions for your system. 31 | 32 | ## 2) Fuzzing 33 | Once AFL is up and running, fuzzing is pretty straightforward: 34 | 35 | ```bash 36 | # Make sure wsServer is in a clean state 37 | make clean 38 | 39 | # Build with AFL_FUZZ var set to yes: 40 | AFL_FUZZ=yes make 41 | ``` 42 | 43 | wsServer and the test file will be compiled. Fuzzing starts automatically 44 | right after. 45 | 46 | --- 47 | 48 | ## Input tests and file structures 49 | All fuzzing-related parts are present in the _tests_ folder and follow the 50 | following structure: 51 | 52 | ```text 53 | ├── in 54 | │   ├── ch_1b_1b_508b_close 55 | │   └── ch_1b_close 56 | │ 57 | ├── out 58 | │ 59 | ├── packets 60 | │   ├── ch_508b_close 61 | │   ├── ch_close 62 | │   ├── ff_1b_close 63 | │   ├── ff_384kB_close 64 | │   ├── ff_ping_ping_close 65 | │   ├── ws_1b_close 66 | │   ├── ws_508b_ping_close 67 | │   ├── ws_98305b_close 68 | │ │ 69 | │   ├── frames 70 | │   │   ├── close 71 | │   │   ├── ping 72 | │   │   ├── req_chrome 73 | │   │   ├── req_firefox 74 | │   │   └── req_websocat 75 | │   │ 76 | │ └── msgs 77 | │      ├── msg_1byte 78 | │      ├── msg_384kB_cont 79 | │      ├── msg_508bytes 80 | │      └── msg_98305bytes 81 | │ 82 | ├── Makefile 83 | ├── run-fuzzy.sh 84 | └── ws_file.c 85 | ``` 86 | 87 | - **in/:** 88 | Contains the input files that will be used in AFL (parameter `-i`). 89 | 90 | - **out/:** 91 | Contains the AFL output (parameter `-o`). Note that the execution script 92 | (`run-fuzzy.sh`) allows you to customize the output by the environment variable 93 | `AFL_OUT`. 94 | 95 | - **packets/:** 96 | Contains packets and parts of packets captured from the network from multiple 97 | clients (currently: Firefox, Google Chrome, and Websocat) with wsServer. It 98 | serves as a way to 'assemble' new test files for wsServer, whether for fuzzing 99 | or not. 100 | 101 | - **packets/frames:/** 102 | Contains request frames (handshake) and control frames from multiple clients. 103 | 104 | - **packets/msgs/:** 105 | Contains packets of messages sent to wsServer of varying sizes, with FRAMES of 106 | type `FIN` and `CONT`. 107 | 108 | ### Creating new inputs 109 | New input files are pretty simple to make: either you create from existing 110 | packets or capture new ones via tcpdump, wireshark, etc. 111 | 112 | Let's say you want to create one that uses a Firefox handshake, sends two messages 113 | of one byte each, one ping and disconnect, we can then do: 114 | ```bash 115 | cd tests/packets 116 | cat frames/req_firefox \ 117 | msgs/msg_1byte msgs/msg_1byte \ 118 | frames/ping \ 119 | frames/close > ../in/ff_1b_1b_ping_close 120 | ``` 121 | 122 | For new packets, the idea is similar. 123 | 124 | **Attention:** Since inputs need to be valid, when creating new packets, be 125 | sure to always use a handshake (req_*) as the first file and a close frame 126 | at the end. 127 | 128 | --- 129 | 130 | ## Acknowledgments 131 | Thanks to [@rlaneth](https://github.com/rlaneth), who performed fuzzing 132 | tests on wsServer and who discovered and helped me to fix many bugs in the 133 | source code. 134 | -------------------------------------------------------------------------------- /doc/TLS.md: -------------------------------------------------------------------------------- 1 | ## SSL/TLS Support 2 | wsServer does not currently support encryption. However, it is possible to use it in conjunction 3 | with [Stunnel](https://www.stunnel.org/), a proxy that adds TLS support to existing projects. 4 | Just follow these four easy steps below to get TLS support on wsServer. 5 | 6 | ### 1) Installing Stunnel 7 | 8 | #### Ubuntu 9 | ```bash 10 | $ sudo apt install stunnel 11 | ``` 12 | 13 | #### Other distros 14 | ```bash 15 | $ sudo something 16 | ``` 17 | 18 | ### 2) Generating certificates/keys 19 | After you have Stunnel installed, generate your CA, private key and copy 20 | to the Stunnel configure folder (usually /etc/stunnel/, but could be anywhere): 21 | 22 | ```bash 23 | # Private key 24 | $ openssl genrsa -out server.key 2048 25 | 26 | # Certificate Signing Request (CSR) 27 | $ openssl req -new -key server.key -out server.csr 28 | 29 | # Certificate 30 | $ openssl x509 -req -days 1024 -in server.csr -signkey server.key -out server.crt 31 | 32 | # Append private key, certificate and copy to the right place 33 | $ cat server.key server.crt > server.pem 34 | $ sudo cp server.pem /etc/stunnel/ 35 | ``` 36 | 37 | Observations regarding localhost: 38 | 39 | 1) If you want to run on localhost, the 'Common Name' field (on CSR, 2nd command) _must_ 40 | be 'localhost' (without quotes). 41 | 42 | 2) Make sure to add your .crt file to your browser's Certificate Authorities before trying 43 | to use wsServer with TLS. 44 | 45 | 3) Google Chrome does not like localhost SSL/TLS traffic, so you need to enable 46 | it first, go to `chrome://flags/#allow-insecure-localhost` and enable this option. 47 | Firefox looks ok as long as you follow 2). 48 | 49 | ### 3) Stunnel configuration file 50 | 51 | Stunnel works by creating a proxy server on a given port that connects to the 52 | original server on another, so we need to teach how it will talk to wsServer: 53 | 54 | Create a file /etc/stunnel/stunnel.conf with the following content: 55 | 56 | ```text 57 | [wsServer] 58 | cert = /etc/stunnel/server.pem 59 | accept = 0.0.0.0:443 60 | connect = 127.0.0.1:8080 61 | ``` 62 | 63 | ### 4) Launch Stunnel and wsServer 64 | 65 | ```bash 66 | $ sudo stunnel /etc/stunnel/stunnel.conf 67 | $ ./your_program_that_uses_wsServer 68 | ``` 69 | 70 | (Many thanks to [@rlaneth](https://github.com/rlaneth) for letting me know of this tool). 71 | -------------------------------------------------------------------------------- /doc/doxygen/Doxyfile-mcss: -------------------------------------------------------------------------------- 1 | @INCLUDE = doxy.conf 2 | GENERATE_HTML = NO 3 | GENERATE_XML = YES 4 | XML_PROGRAMLISTING = YES 5 | OUTPUT_DIRECTORY = ../../doc/doxygen 6 | -------------------------------------------------------------------------------- /doc/doxygen/templates/annotated.html: -------------------------------------------------------------------------------- 1 | {% set navbar_current = 'annotated' %} 2 | {% extends 'base-index.html' %} 3 | 4 | {% block main %} 5 |

Classes

6 |
    7 | {% for i in index.symbols recursive %} 8 | {% if i.children %} 9 |
  • 10 | {{ i.kind }} {{ i.name }}{% if i.is_inline %} inline{% endif %}{% if i.is_final %} final{% endif %}{% if i.deprecated %} {{ i.deprecated }}{% endif %}{% if i.since %} {{ i.since }}{% endif %} {{ i.brief }} 11 |
      12 | {{ loop(i.children)|rtrim|indent(4, true) }} 13 |
    14 |
  • 15 | {% else %} 16 |
  • {{ i.kind }} {{ i.name }}{% if i.is_inline %} inline{% endif %}{% if i.is_final %} final{% endif %}{% if i.deprecated %} {{ i.deprecated }}{% endif %}{% if i.since %} {{ i.since }}{% endif %} {{ i.brief }}
  • 17 | {% endif %} 18 | {% endfor %} 19 |
20 | {{ super() -}} 21 | {% endblock %} 22 | -------------------------------------------------------------------------------- /doc/doxygen/templates/base-index.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block main %} 4 | 20 | {% endblock %} 21 | -------------------------------------------------------------------------------- /doc/doxygen/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% block title %}{{ PROJECT_NAME }}{% if PROJECT_BRIEF %} {{ PROJECT_BRIEF }}{% endif %}{% endblock %} 6 | {% for css in STYLESHEETS %} 7 | 8 | {% endfor %} 9 | {% if FAVICON %} 10 | 11 | {% endif %} 12 | {% if not SEARCH_DISABLED and SEARCH_BASE_URL %} 13 | 14 | {% endif %} 15 | {% block header_links %} 16 | {% endblock %} 17 | 18 | {% if THEME_COLOR %} 19 | 20 | {% endif %} 21 | {% if HTML_HEADER %} 22 | {{ HTML_HEADER|rtrim|indent(2) }} 23 | {% endif %} 24 | 27 | 28 | 29 |
108 |
109 |
110 |
111 |
112 | {% if PAGE_HEADER %} 113 | {{ PAGE_HEADER|replace('{filename}', FILENAME) }} 114 | {% endif %} 115 | {% block main %} 116 | {% endblock %} 117 |
118 |
119 |
120 |
121 | {% if not SEARCH_DISABLED %} 122 | 146 | 147 | {% if SEARCH_DOWNLOAD_BINARY %} 148 | 151 | {% else %} 152 | 153 | {% endif %} 154 | {% endif %} 155 | {% if FINE_PRINT %} 156 |
169 | {% endif %} 170 | 171 | 172 | {#- sanity checks for variables that should be always defined -#} 173 | {% if FILENAME is not defined %}{{ FILENAME.is_not_defined_the_script_is_broken }}{% endif %} 174 | {% if DOXYGEN_VERSION is not defined %}{{ DOXYGEN_VERSION.is_not_defined_the_script_is_broken }}{% endif %} 175 | {% if SEARCHDATA_FORMAT_VERSION is not defined %}{{ SEARCHDATA_FORMAT_VERSION.is_not_defined_the_script_is_broken }}{% endif %} 176 | -------------------------------------------------------------------------------- /doc/doxygen/templates/class.html: -------------------------------------------------------------------------------- 1 | {% extends 'base-class-reference.html' %} 2 | -------------------------------------------------------------------------------- /doc/doxygen/templates/details-define.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | {% set j = joiner(',\n ') %} 4 | #define {{ define.name }}{% if define.params != None %}({% for param in define.params %}{{ j() }}{{ param[0] }}{% endfor %}){% endif %}{% if define.since %} {{ define.since }}{% endif %} 5 | {% if define.include %} 6 | 7 | {% endif %} 8 |

9 | {% if define.brief %} 10 |

{{ define.brief }}

11 | {% endif %} 12 | {% if define.has_param_details or define.return_value %} 13 | 14 | {% if define.has_param_details %} 15 | 16 | 17 | 18 | 19 | {% for name, description in define.params %} 20 | 21 | {{ name }} 22 | 23 | 24 | {% endfor %} 25 | 26 | {% endif %} 27 | {% if define.return_value %} 28 | 29 | 30 | Returns 31 | 32 | 33 | 34 | {% endif %} 35 |
Parameters
{{ description }}
{{ define.return_value }}
36 | {% endif %} 37 | {% if define.description %} 38 | {{ define.description }} 39 | {% endif %} 40 |
41 | -------------------------------------------------------------------------------- /doc/doxygen/templates/details-enum.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | {% if compound.templates != None %} 4 |
5 | {% set j = joiner(', ') %} 6 | template<{% for t in compound.templates %}{{ j() }}{{ t.type }} {% if t.name %}{{ t.name }}{% else %}_{{ loop.index }}{% endif %}{% endfor %}> 7 |
8 | {% endif %} 9 | enum {% if enum.is_strong %}class {% endif %}{{ prefix }}{{ enum.name }}{% if enum.type %}: {{ enum.type }}{% endif %}{% if enum.is_protected %} protected{% endif %}{% if enum.since %} {{ enum.since }}{% endif %} 10 | {# not sure why there needs to be this space #} 11 | 12 | {% if enum.include %} 13 | {# Template info can be only present if the enum is inside a 14 | templated class, but in that case we have global include 15 | information, so no need to handle case where 16 | `enum.include and compound.templates != None` #} 17 | 18 | {% endif %} 19 |

20 | {% if enum.brief %}{# brief can be omitted for anonymous enums #} 21 |

{{ enum.brief }}

22 | {% endif %} 23 | {% if enum.description %} 24 | {{ enum.description }} 25 | {% endif %} 26 | {% if enum.has_value_details %} 27 | 28 | 29 | 30 | {% for value in enum.values %} 31 | 32 | 33 | 41 | 42 | {% endfor %} 43 | 44 |
Enumerators
{{ value.name }}{% if value.since %} {{ value.since }}{% endif %} 34 | {% if value.brief %}{# brief is not required for values #} 35 |

{{ value.brief }}

36 | {% endif %} 37 | {% if value.description %}{# it can be only brief tho #} 38 | {{ value.description }} 39 | {% endif %} 40 |
45 | {% endif %} 46 |
47 | -------------------------------------------------------------------------------- /doc/doxygen/templates/details-func.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | {% if compound.templates != None or func.templates != None %} 4 | {% if func.include %} 5 | 6 | {% endif %} 7 |
8 | {% if compound.templates != None %} 9 | {% set j = joiner(', ') %} 10 | template<{% for t in compound.templates %}{{ j() }}{{ t.type }} {% if t.name %}{{ t.name }}{% else %}_{{ loop.index }}{% endif %}{% endfor %}> 11 | {% endif %} 12 | {% if func.templates != None %} 13 | {% set j = joiner(', ') %} 14 | template<{% for t in func.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif %}{% endfor %}> 15 | {% endif %} 16 |
17 | {% endif %} 18 | {% set j = joiner(',\n ') %} 19 | {{ func.prefix }}{{ func.type }} {{ prefix }}{{ func.name }}({% for param in func.params %}{{ j() }}{{ param.type_name }}{% if param.default %} = {{ param.default }}{% endif %}{% endfor %}){{ func.suffix }}{% if func.is_explicit %} explicit {% endif %}{% if func.is_final %} final{% elif func.is_override %} override{% elif func.is_pure_virtual %} pure virtual{% elif func.is_virtual %} virtual{% endif %}{% if func.is_protected %} protected{% if func.is_slot %} slot{% endif %}{% elif func.is_private %} private{% if func.is_slot %} slot{% endif %}{% elif func.is_signal %} signal{% elif func.is_slot %} public slot{% endif %}{% if func.is_defaulted %} defaulted{% endif %}{% if func.is_deleted %} deleted{% endif %}{% if func.is_constexpr %} constexpr{% endif %}{% if func.is_conditional_noexcept %} noexcept(…){% elif func.is_noexcept %} noexcept{% endif %}{% if func.since %} {{ func.since }}{% endif %} 20 | {% if func.include and compound.templates == None and func.templates == None %} 21 | 22 | {% endif %} 23 |

24 | {% if func.brief %} 25 |

{{ func.brief }}

26 | {% endif %} 27 | {% if func.has_template_details or func.has_param_details or func.return_value or func.return_values or func.exceptions %} 28 | 29 | {% if func.has_template_details %} 30 | 31 | 32 | 33 | 34 | {% for template in func.templates|selectattr('name') %} 35 | 36 | {{ template.name }} 37 | 38 | 39 | {% endfor %} 40 | 41 | {% endif %} 42 | {% if func.has_param_details %} 43 | 44 | 45 | 46 | 47 | {% for param in func.params|selectattr('name') %} 48 | 49 | {{ param.name }}{% if param.direction == 'in' %} in{% elif param.direction == 'out' %} out{% elif param.direction == 'inout' %} in/out{% endif %} 50 | 51 | 52 | {% endfor %} 53 | 54 | {% endif %} 55 | {% if func.return_value %} 56 | {{ '' if func.return_values or func.exceptions else '' }} 57 | 58 | Returns 59 | 60 | 61 | {{ '' if func.return_values or func.exceptions else '' }} 62 | {% elif func.return_values %} 63 | 64 | 65 | 66 | {% endif %} 67 | {% if func.return_values %} 68 | 69 | {% for value, description in func.return_values %} 70 | 71 | {{ value }} 72 | 73 | 74 | {% endfor %} 75 | 76 | {% endif %} 77 | {% if func.exceptions %} 78 | 79 | 80 | 81 | 82 | {% for exception, description in func.exceptions %} 83 | 84 | {{ exception }} 85 | 86 | 87 | {% endfor %} 88 | 89 | {% endif %} 90 |
Template parameters
{{ template.description }}
Parameters
{{ param.description }}
{{ func.return_value }}
Returns
{{ description }}
Exceptions
{{ description }}
91 | {% endif %} 92 | {% if func.description %} 93 | {{ func.description }} 94 | {% endif %} 95 |
96 | -------------------------------------------------------------------------------- /doc/doxygen/templates/details-typedef.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | {% if compound.templates != None or typedef.templates != None %} 4 | {% if typedef.include %} 5 | 6 | {% endif %} 7 |
8 | {% if compound.templates != None %} 9 | {% set j = joiner(', ') %} 10 | template<{% for t in compound.templates %}{{ j() }}{{ t.type }} {% if t.name %}{{ t.name }}{% else %}_{{ loop.index }}{% endif %}{% endfor %}> 11 | {% endif %} 12 | {% if typedef.templates != None %} 13 | {% set j = joiner(', ') %} 14 | template<{% for t in typedef.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif %}{% endfor %}> 15 | {% endif %} 16 |
17 | {% endif %} 18 | {% if typedef.is_using %} 19 | using {{ prefix }}{{ typedef.name }} = {{ typedef.type }}{{ typedef.args }}{% if typedef.is_protected %} protected{% endif %}{% if typedef.since %} {{ typedef.since }}{% endif %} 20 | {% else %} 21 | typedef {{ typedef.type }}{% if not typedef.args %} {% endif %}{{ prefix }}{{ typedef.name }}{{ typedef.args }}{% if typedef.is_protected %} protected{% endif %}{% if typedef.since %} {{ typedef.since }}{% endif %} 22 | {% endif %} 23 | {# the empty line has to be here to prevent the lines from merging #} 24 | 25 | {% if typedef.include and compound.templates == None and typedef.templates == None %} 26 | 27 | {% endif %} 28 |

29 | {% if typedef.brief %} 30 |

{{ typedef.brief }}

31 | {% endif %} 32 | {% if typedef.has_template_details %} 33 | 34 | 35 | 36 | 37 | 38 | {% for template in typedef.templates|selectattr('name') %} 39 | 40 | {{ template.name }} 41 | 42 | 43 | {% endfor %} 44 | 45 |
Template parameters
{{ template.description }}
46 | {% endif %} 47 | {% if typedef.description %} 48 | {{ typedef.description }} 49 | {% endif %} 50 |
51 | -------------------------------------------------------------------------------- /doc/doxygen/templates/details-var.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | {% if compound.templates != None or var.templates != None %} 4 | {% if var.include %} 5 | 6 | {% endif %} 7 |
8 | {% if compound.templates != None %} 9 | {% set j = joiner(', ') %} 10 | template<{% for t in compound.templates %}{{ j() }}{{ t.type }} {% if t.name %}{{ t.name }}{% else %}_{{ loop.index }}{% endif %}{% endfor %}> 11 | {% endif %} 12 | {% if var.templates != None %} 13 | {% set j = joiner(', ') %} 14 | template<{% for t in var.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif %}{% endfor %}> 15 | {% endif %} 16 |
17 | {% endif %} 18 | {%+ if var.is_static %}static {% endif %}{{ var.type }} {{ prefix }}{{ var.name }}{% if var.is_protected %} protected{% endif %}{% if var.is_constexpr %} constexpr{% endif %}{% if var.since %} {{ var.since }}{% endif %} 19 | {# the empty line needs to be here to prevent the lines from merging #} 20 | 21 | {% if var.include and compound.templates == None and var.templates == None %} 22 | 23 | {% endif %} 24 |

25 | {% if var.brief %} 26 |

{{ var.brief }}

27 | {% endif %} 28 | {% if var.has_template_details %} 29 | 30 | 31 | 32 | 33 | 34 | {% for template in var.templates|selectattr('name') %} 35 | 36 | {{ template.name }} 37 | 38 | 39 | {% endfor %} 40 | 41 |
Template parameters
{{ template.description }}
42 | {% endif %} 43 | {% if var.description %} 44 | {{ var.description }} 45 | {% endif %} 46 |
47 | -------------------------------------------------------------------------------- /doc/doxygen/templates/dir.html: -------------------------------------------------------------------------------- 1 | {% extends 'base-reference.html' %} 2 | 3 | {% block title %}{% for name, _ in compound.breadcrumb %}{{ name }}/{% endfor %} directory | {{ super() }}{% endblock %} 4 | 5 | {% block header %} 6 |

7 | {%+ for name, target in compound.breadcrumb[:-1] %}{{ name }}/{% endfor %}{{ compound.breadcrumb[-1][0] }}/ directory{% if compound.since %} {{ compound.since }}{% endif %} 8 | {# need an explicit space here otherwise the newline gets removed #} 9 | 10 |

11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /doc/doxygen/templates/entry-class.html: -------------------------------------------------------------------------------- 1 |
2 | {% if class.templates != None %} 3 | {% set j = joiner(', ') %} 4 |
template<{% for t in class.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif %}{% endfor %}>
5 | {% endif %} 6 | {{ class.kind }} {{ class.name }}{% if class.is_protected %} protected{% endif %}{% if class.is_final %} final{% elif class.is_virtual %} virtual{% endif %}{% if class.deprecated %} {{ class.deprecated }}{% endif %}{% if class.since %} {{ class.since }}{% endif %} 7 | 8 | {# the empty line is above to fix spacing #} 9 |
10 |
{{ class.brief }}
11 | -------------------------------------------------------------------------------- /doc/doxygen/templates/entry-define.html: -------------------------------------------------------------------------------- 1 | 2 | {% set j = joiner(',\n ') %} 3 | #define {{ define.name }}{% if define.params != None %}({% for param in define.params %}{{ j() }}{{ param[0] }}{% endfor %}){% endif %}{% if define.deprecated %} {{ define.deprecated }}{% endif %}{% if define.since %} {{ define.since }}{% endif %} 4 | 5 |
{{ define.brief }}
6 | -------------------------------------------------------------------------------- /doc/doxygen/templates/entry-dir.html: -------------------------------------------------------------------------------- 1 |
directory {{ dir.name }}/{% if dir.deprecated %} {{ dir.deprecated }}{% endif %}{% if dir.since %} {{ dir.since }}{% endif %}
2 |
{{ dir.brief }}
3 | -------------------------------------------------------------------------------- /doc/doxygen/templates/entry-enum.html: -------------------------------------------------------------------------------- 1 | 2 | {% set j = joiner(',\n ') %} 3 | enum {% if enum.is_strong %}class {% endif %}{{ enum.name }}{% if enum.type %}: {{ enum.type }}{% endif %} { {% for value in enum.values %}{{ j() }}{{ value.name }}{% if value.initializer %} {{ value.initializer }}{% endif %}{% if value.since %} {{ value.since }}{% endif %}{% if value.deprecated %} {{ value.deprecated }}{% endif %}{% endfor %} }{% if enum.deprecated %} {{ enum.deprecated }}{% endif %}{% if mark_nonpublic and enum.is_protected %} protected{% endif %}{% if enum.since %} {{ enum.since }}{% endif %} 4 | 5 |
{{ enum.brief }}
6 | -------------------------------------------------------------------------------- /doc/doxygen/templates/entry-file.html: -------------------------------------------------------------------------------- 1 |
file {{ file.name }}{% if file.deprecated %} {{ file.deprecated }}{% endif %}{% if file.since %} {{ file.since }}{% endif %}
2 |
{{ file.brief }}
3 | -------------------------------------------------------------------------------- /doc/doxygen/templates/entry-func.html: -------------------------------------------------------------------------------- 1 | 2 | {% if func.templates != None %} 3 | {% set j = joiner(', ') %} 4 |
template<{% for t in func.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif %}{% endfor %}>
5 | {% endif %} 6 | {% set j = joiner(',\n ') %} 7 | {{ func.prefix }}{% if func.type == 'void' %}void {% elif func.type %}auto {% endif %}{{ func.name }}({% for param in func.params %}{{ j() }}{{ param.type_name }}{% if param.default %} = {{ param.default }}{% endif %}{% endfor %}){{ func.suffix }}{% if func.type and func.type != 'void' %} -> {{ func.type }}{% endif %}{% if func.deprecated %} {{ func.deprecated }}{% endif %}{% if not func.type or mark_nonpublic %}{% if func.is_protected %} protected{% if func.is_slot %} slot{% endif %}{% elif func.is_private %} private{% if func.is_slot %} slot{% endif %}{% elif func.is_signal %} signal{% elif func.is_slot %} public slot{% endif %}{% endif %}{% if func.is_defaulted %} defaulted{% endif %}{% if func.is_deleted %} deleted{% endif %}{% if func.is_explicit %} explicit {% endif %}{% if func.is_final %} final{% elif func.is_override %} override{% elif func.is_pure_virtual %} pure virtual{% elif func.is_virtual %} virtual{% endif %}{% if func.is_constexpr %} constexpr{% endif %}{% if func.is_conditional_noexcept %} noexcept(…){% elif func.is_noexcept %} noexcept{% endif %}{% if func.since %} {{ func.since }}{% endif %} 8 | 9 |
{{ func.brief }}
10 | -------------------------------------------------------------------------------- /doc/doxygen/templates/entry-module.html: -------------------------------------------------------------------------------- 1 |
module {{ module.name }}{% if module.deprecated %} {{ module.deprecated }}{% endif %}{% if module.since %} {{ module.since }}{% endif %}
2 |
{{ module.brief }}
3 | -------------------------------------------------------------------------------- /doc/doxygen/templates/entry-namespace.html: -------------------------------------------------------------------------------- 1 |
namespace {{ namespace.name }}{% if namespace.is_inline %} inline{% endif %}{% if namespace.deprecated %} {{ namespace.deprecated }}{% endif %}{% if namespace.since %} {{ namespace.since }}{% endif %}
2 |
{{ namespace.brief }}
3 | -------------------------------------------------------------------------------- /doc/doxygen/templates/entry-typedef.html: -------------------------------------------------------------------------------- 1 | 2 | {% if typedef.templates != None %} 3 | {% set j = joiner(', ') %} 4 |
template<{% for t in typedef.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif%}{% endfor %}>
5 | {% endif %} 6 | using {{ typedef.name }} = {{ typedef.type }}{{ typedef.args }}{% if typedef.deprecated %} {{ typedef.deprecated }}{% endif %}{% if mark_nonpublic and typedef.is_protected %} protected{% endif %}{% if typedef.since %} {{ typedef.since }}{% endif %} 7 | {# This empty line needs to be there otherwise it's eaten #} 8 | 9 | 10 |
{{ typedef.brief }}
11 | -------------------------------------------------------------------------------- /doc/doxygen/templates/entry-var.html: -------------------------------------------------------------------------------- 1 | 2 | {% if var.templates != None %} 3 | {% set j = joiner(', ') %} 4 |
template<{% for t in var.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif%}{% endfor %}>
5 | {% endif %} 6 | {%+ if var.is_static %}static {% endif %}{{ var.type }} {{ var.name }}{% if var.deprecated %} {{ var.deprecated }}{% endif %}{% if mark_nonpublic and var.is_protected %} protected{% endif %}{% if var.is_constexpr %} constexpr{% endif %}{% if var.since %} {{ var.since }}{% endif %} 7 | {# This empty line needs to be there otherwise it's eaten #} 8 | 9 | 10 |
{{ var.brief }}
11 | -------------------------------------------------------------------------------- /doc/doxygen/templates/example.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %}{% set j = joiner(' » ') %}{% for name, _ in compound.breadcrumb %}{{ j() }}{{ name }}{% endfor %} source | {{ super() }}{% endblock %} 4 | 5 | {% block header_links %} 6 | {% if compound.footer_navigation and compound.footer_navigation[0] %} 7 | 8 | {% endif %} 9 | {% if compound.footer_navigation and compound.footer_navigation[2] %} 10 | 11 | {% endif %} 12 | {% endblock %} 13 | 14 | {% block main %} 15 |

16 | {% for name, target in compound.breadcrumb[:-1] %} 17 | {{ name }} » 18 | {% endfor %} 19 | {{ compound.breadcrumb[-1][0] }} source 20 |

21 | {% if compound.brief %} 22 |

{{ compound.brief }}

23 | {% endif %} 24 | {% if compound.description %} 25 | {{ compound.description }} 26 | {% endif %} 27 | {% if compound.footer_navigation %} 28 |
{% if compound.footer_navigation[0] %}« {{ compound.footer_navigation[0][1] }} | {% endif %}{{ compound.footer_navigation[1][1] }}{% if compound.footer_navigation[2] %} | {{ compound.footer_navigation[2][1] }} »{% endif %}
29 | {% endif %} 30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /doc/doxygen/templates/file.html: -------------------------------------------------------------------------------- 1 | {% extends 'base-reference.html' %} 2 | 3 | {% block title %}{% set j = joiner('/') %}{% for name, _ in compound.breadcrumb %}{{ j() }}{{ name }}{% endfor %} file | {{ super() }}{% endblock %} 4 | 5 | {% block header %} 6 |

7 | {%+ for name, target in compound.breadcrumb[:-1] %}{{ name }}/{% endfor %}{{ compound.breadcrumb[-1][0] }} file{% if compound.since %} {{ compound.since }}{% endif %} 8 | {# need an explicit space here otherwise the newline gets removed #} 9 | 10 |

11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /doc/doxygen/templates/files.html: -------------------------------------------------------------------------------- 1 | {% set navbar_current = 'files' %} 2 | {% extends 'base-index.html' %} 3 | 4 | {% block main %} 5 |

Files

6 |
    7 | {% for i in index.files recursive %} 8 | {% if i.children %} 9 |
  • 10 | {{ i.kind }} {{ i.name }}{% if i.deprecated %} {{ i.deprecated }}{% endif %}{% if i.since %} {{ i.since }}{% endif %} {{ i.brief }} 11 |
      12 | {{ loop(i.children)|rtrim|indent(4, true) }} 13 |
    14 |
  • 15 | {% else %} 16 |
  • {{ i.kind }} {{ i.name }}{% if i.deprecated %} {{ i.deprecated }}{% endif %}{% if i.since %} {{ i.since }}{% endif %} {{ i.brief }}
  • 17 | {% endif %} 18 | {% endfor %} 19 |
20 | {{ super() -}} 21 | {% endblock %} 22 | -------------------------------------------------------------------------------- /doc/doxygen/templates/group.html: -------------------------------------------------------------------------------- 1 | {% extends 'base-reference.html' %} 2 | 3 | {% block title %}{% set j = joiner(' » ') %}{% for name, _ in compound.breadcrumb %}{{ j() }}{{ name }}{% endfor %} module | {{ super() }}{% endblock %} 4 | 5 | {% block header %} 6 |

7 | {% for name, target in compound.breadcrumb[:-1] %} 8 | {{ name }} » 9 | {% endfor %} 10 | {{ compound.breadcrumb[-1][0] }} module{% if compound.since %} {{ compound.since }}{% endif %}

11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /doc/doxygen/templates/modules.html: -------------------------------------------------------------------------------- 1 | {% set navbar_current = 'modules' %} 2 | {% extends 'base-index.html' %} 3 | 4 | {% block main %} 5 |

Modules

6 |
    7 | {% for i in index.modules recursive %} 8 | {% if i.has_nestable_children %} 9 |
  • 10 | module {{ i.name }}{% if i.deprecated %} {{ i.deprecated }}{% endif %}{% if i.since %} {{ i.since }}{% endif %} {{ i.brief }} 11 |
      12 | {{ loop(i.children)|rtrim|indent(4, true) }} 13 |
    14 |
  • 15 | {% else %} 16 |
  • module {{ i.name }}{% if i.deprecated %} {{ i.deprecated }}{% endif %}{% if i.since %} {{ i.since }}{% endif %} {{ i.brief }}
  • 17 | {% endif %} 18 | {% endfor %} 19 |
20 | {{ super() -}} 21 | {% endblock %} 22 | -------------------------------------------------------------------------------- /doc/doxygen/templates/namespace.html: -------------------------------------------------------------------------------- 1 | {% extends 'base-reference.html' %} 2 | 3 | {% block title %}{% set j = joiner('::') %}{% for name, _ in compound.breadcrumb %}{{ j() }}{{ name }}{% endfor %} namespace | {{ super() }}{% endblock %} 4 | 5 | {% block header %} 6 |

7 | {%+ for name, target in compound.breadcrumb[:-1] %}{{ name }}::{% endfor %}{{ compound.breadcrumb[-1][0] }} namespace{% if compound.is_inline %} inline{% endif %}{% if compound.since %} {{ compound.since }}{% endif %} 8 | {# need an explicit space here otherwise the newline gets removed #} 9 | 10 | {% if compound.include %} 11 | 12 | {% endif %} 13 |

14 | {% endblock %} 15 | -------------------------------------------------------------------------------- /doc/doxygen/templates/namespaces.html: -------------------------------------------------------------------------------- 1 | {% set navbar_current = 'namespaces' %} 2 | {% extends 'base-index.html' %} 3 | 4 | {% block main %} 5 |

Namespaces

6 |
    7 | {% for i in index.symbols|selectattr('kind', 'equalto', 'namespace') recursive %} 8 | {% if i.has_nestable_children %} 9 |
  • 10 | {{ i.kind }} {{ i.name }}{% if i.is_inline %} inline{% endif %}{% if i.deprecated %} {{ i.deprecated }}{% endif %}{% if i.since %} {{ i.since }}{% endif %} {{ i.brief }} 11 |
      12 | {{ loop(i.children|selectattr('kind', 'equalto', 'namespace'))|rtrim|indent(4, true) }} 13 |
    14 |
  • 15 | {% else %} 16 |
  • {{ i.kind }} {{ i.name }}{% if i.is_inline %} inline{% endif %}{% if i.deprecated %} {{ i.deprecated }}{% endif %}{% if i.since %} {{ i.since }}{% endif %} {{ i.brief }}
  • 17 | {% endif %} 18 | {% endfor %} 19 |
20 | {{ super() -}} 21 | {% endblock %} 22 | -------------------------------------------------------------------------------- /doc/doxygen/templates/opensearch.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ PROJECT_NAME }}{% if PROJECT_BRIEF %} {{ PROJECT_BRIEF }}{% endif %} 4 | Search {{ PROJECT_NAME }} documentation 5 | {% if FAVICON %} 6 | {{ SEARCH_BASE_URL|urljoin(FAVICON[0])|e }} 7 | {% endif %} 8 | 9 | 10 | -------------------------------------------------------------------------------- /doc/doxygen/templates/page.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %}{% if 1 in compound.breadcrumb or compound.breadcrumb[-1][0] != PROJECT_NAME %}{% set j = joiner(' » ') %}{% for name, _ in compound.breadcrumb %}{{ j() }}{{ name }}{% endfor %} | {{ super() }}{% else %}{{ super() }}{% endif %}{% endblock %} 4 | 5 | {% block header_links %} 6 | {% if compound.footer_navigation and compound.footer_navigation[0] %} 7 | 8 | {% endif %} 9 | {% if compound.footer_navigation and compound.footer_navigation[2] %} 10 | 11 | {% endif %} 12 | {% endblock %} 13 | 14 | {% block main %} 15 |

16 | {% for name, target in compound.breadcrumb[:-1] %} 17 | {{ name }} » 18 | {% endfor %} 19 | {{ compound.breadcrumb[-1][0] }}{% if compound.since %} {{ compound.since }}{% endif %} 20 | {# need an explicit space here otherwise the newline gets removed #} 21 | 22 |

23 | {% if compound.brief %} 24 |

{{ compound.brief }}

25 | {% endif %} 26 | {% if compound.sections %} 27 |
28 |

Contents

29 |
    30 | {% for id, name, children in compound.sections recursive %} 31 | {% if children %} 32 |
  • 33 | {{ name }} 34 |
      35 | {{ loop(children)|rtrim|indent(4, true) }} 36 |
    37 |
  • 38 | {% else %} 39 |
  • {{ name }}
  • 40 | {% endif %} 41 | {% endfor %} 42 |
43 |
44 | {% endif %} 45 | {% if compound.description %} 46 | {{ compound.description }} 47 | {% endif %} 48 | {% if compound.footer_navigation %} 49 |
{% if compound.footer_navigation[0] %}« {{ compound.footer_navigation[0][1] }} | {% endif %}{{ compound.footer_navigation[1][1] }}{% if compound.footer_navigation[2] %} | {{ compound.footer_navigation[2][1] }} »{% endif %}
50 | {% endif %} 51 | {% endblock %} 52 | -------------------------------------------------------------------------------- /doc/doxygen/templates/pages.html: -------------------------------------------------------------------------------- 1 | {% set navbar_current = 'pages' %} 2 | {% extends 'base-index.html' %} 3 | 4 | {% block main %} 5 |

Pages

6 |
    7 | {% for i in index.pages recursive %} 8 | {% if i.children %} 9 |
  • 10 | {{ i.name }}{% if i.deprecated %} {{ i.deprecated }}{% endif %}{% if i.since %} {{ i.since }}{% endif %} {{ i.brief }} 11 |
      12 | {{ loop(i.children)|rtrim|indent(4, true) }} 13 |
    14 |
  • 15 | {% else %} 16 |
  • {{ i.name }}{% if i.deprecated %} {{ i.deprecated }}{% endif %}{% if i.since %} {{ i.since }}{% endif %} {{ i.brief }}
  • 17 | {% endif %} 18 | {% endfor %} 19 |
20 | {{ super() -}} 21 | {% endblock %} 22 | -------------------------------------------------------------------------------- /doc/doxygen/templates/struct.html: -------------------------------------------------------------------------------- 1 | {% extends 'base-class-reference.html' %} 2 | -------------------------------------------------------------------------------- /doc/doxygen/templates/union.html: -------------------------------------------------------------------------------- 1 | {% extends 'base-class-reference.html' %} 2 | -------------------------------------------------------------------------------- /doc/man/man3/ws_close_client.3: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (C) 2016-2022 Davidson Francis 3 | .\" 4 | .\" This program is free software: you can redistribute it and/or modify 5 | .\" it under the terms of the GNU General Public License as published by 6 | .\" the Free Software Foundation, either version 3 of the License, or 7 | .\" (at your option) any later version. 8 | .\" 9 | .\" This program is distributed in the hope that it will be useful, 10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | .\" GNU General Public License for more details. 13 | .\" 14 | .\" You should have received a copy of the GNU General Public License 15 | .\" along with this program. If not, see 16 | .\" 17 | .TH man 3 "04 Apr 2022" "1.0" "wsServer man page" 18 | .SH NAME 19 | ws_close_connection \- Close the client connection. 20 | .SH SYNOPSIS 21 | .nf 22 | .B #include 23 | .sp 24 | .BI "int ws_close_client(ws_cli_conn_t " client "); 25 | .fi 26 | .SH DESCRIPTION 27 | .BR ws_close_client () 28 | for a given client 29 | .I client 30 | , closes the client connection with normal close code (1000) and no 31 | reason string. 32 | .SH RETURN VALUE 33 | Returns 0 if success, -1 otherwise. 34 | .SH NOTES 35 | If the client did not send a close frame in TIMEOUT_MS ms (500 ms), the 36 | server will close the connection with error code (1002). 37 | .SH AUTHOR 38 | Davidson Francis (davidsondfgl@gmail.com) 39 | -------------------------------------------------------------------------------- /doc/man/man3/ws_get_state.3: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (C) 2016-2022 Davidson Francis 3 | .\" 4 | .\" This program is free software: you can redistribute it and/or modify 5 | .\" it under the terms of the GNU General Public License as published by 6 | .\" the Free Software Foundation, either version 3 of the License, or 7 | .\" (at your option) any later version. 8 | .\" 9 | .\" This program is distributed in the hope that it will be useful, 10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | .\" GNU General Public License for more details. 13 | .\" 14 | .\" You should have received a copy of the GNU General Public License 15 | .\" along with this program. If not, see 16 | .\" 17 | .TH man 3 "04 Apr 2022" "1.0" "wsServer man page" 18 | .SH NAME 19 | ws_get_state \- Get a client current state 20 | .SH SYNOPSIS 21 | .nf 22 | .B #include 23 | .sp 24 | .BI "int ws_get_state(ws_cli_conn_t " client "); 25 | .fi 26 | .SH DESCRIPTION 27 | .BR ws_get_state () 28 | for a given client 29 | .I client 30 | , gets the current state. Valid states are: 31 | .PP 32 | .RS 2 33 | .IP \(em 2 34 | WS_STATE_CONNECTING (0) 35 | .IP \(em 2 36 | WS_STATE_OPEN (1) 37 | .IP \(em 2 38 | WS_STATE_CLOSING (2) 39 | .IP \(em 2 40 | WS_STATE_CLOSED (3) 41 | .PP 42 | Anything other than that should be considered an error. 43 | .SH RETURN VALUE 44 | Returns the client state or -1 if error. 45 | .SH AUTHOR 46 | Davidson Francis (davidsondfgl@gmail.com) 47 | -------------------------------------------------------------------------------- /doc/man/man3/ws_getaddress.3: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (C) 2016-2022 Davidson Francis 3 | .\" 4 | .\" This program is free software: you can redistribute it and/or modify 5 | .\" it under the terms of the GNU General Public License as published by 6 | .\" the Free Software Foundation, either version 3 of the License, or 7 | .\" (at your option) any later version. 8 | .\" 9 | .\" This program is distributed in the hope that it will be useful, 10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | .\" GNU General Public License for more details. 13 | .\" 14 | .\" You should have received a copy of the GNU General Public License 15 | .\" along with this program. If not, see 16 | .\" 17 | .TH man 3 "04 Apr 2022" "1.0" "wsServer man page" 18 | .SH NAME 19 | ws_getaddress \- Gets the client IP address 20 | .SH SYNOPSIS 21 | .nf 22 | .B #include 23 | .sp 24 | .BI "char* ws_getaddress(ws_cli_conn_t " "client" ); 25 | .fi 26 | .SH DESCRIPTION 27 | .BR ws_getaddress () 28 | obtains the client IP address as a string for a given 29 | .I client 30 | passed as parameter. 31 | .SH NOTES 32 | .PP 33 | The returned string is static, no need to free up memory. 34 | .SH RETURN VALUE 35 | Returns a string containing the client IP address or null if error. 36 | .SH AUTHOR 37 | Davidson Francis (davidsondfgl@gmail.com) 38 | -------------------------------------------------------------------------------- /doc/man/man3/ws_getport.3: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (C) 2016-2022 Davidson Francis 3 | .\" 4 | .\" This program is free software: you can redistribute it and/or modify 5 | .\" it under the terms of the GNU General Public License as published by 6 | .\" the Free Software Foundation, either version 3 of the License, or 7 | .\" (at your option) any later version. 8 | .\" 9 | .\" This program is distributed in the hope that it will be useful, 10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | .\" GNU General Public License for more details. 13 | .\" 14 | .\" You should have received a copy of the GNU General Public License 15 | .\" along with this program. If not, see 16 | .\" 17 | .TH man 3 "28 Nov 2023" "1.0" "wsServer man page" 18 | .SH NAME 19 | ws_getaddress \- Gets the client connected port 20 | .SH SYNOPSIS 21 | .nf 22 | .B #include 23 | .sp 24 | .BI "char* ws_getport(ws_cli_conn_t " "client" ); 25 | .fi 26 | .SH DESCRIPTION 27 | .BR ws_getport () 28 | returns the connection port relative to a given 29 | .I client 30 | passed as parameter. 31 | .SH NOTES 32 | .PP 33 | The returned string is static, no need to free up memory. 34 | .SH RETURN VALUE 35 | Returns a string containing the client connection port or an empty 36 | string if error. 37 | .SH AUTHOR 38 | Davidson Francis (davidsondfgl@gmail.com) 39 | -------------------------------------------------------------------------------- /doc/man/man3/ws_ping.3: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (C) 2022 Davidson Francis 3 | .\" 4 | .\" This program is free software: you can redistribute it and/or modify 5 | .\" it under the terms of the GNU General Public License as published by 6 | .\" the Free Software Foundation, either version 3 of the License, or 7 | .\" (at your option) any later version. 8 | .\" 9 | .\" This program is distributed in the hope that it will be useful, 10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | .\" GNU General Public License for more details. 13 | .\" 14 | .\" You should have received a copy of the GNU General Public License 15 | .\" along with this program. If not, see 16 | .\" 17 | .TH man 3 "29 Apr 2022" "1.0" "wsServer man page" 18 | .SH NAME 19 | ws_ping \- Sends a ping to a single client or broadcast 20 | .SH SYNOPSIS 21 | .nf 22 | .B #include 23 | .sp 24 | .BI "void ws_ping(ws_cli_conn_t " client ", int " broadcast ");" 25 | .fi 26 | .SH DESCRIPTION 27 | .BR ws_ping () 28 | sends a ping message to the 29 | .I client 30 | (or broadcast if NULL) with a given 31 | .I threshold. 32 | Although wsServer supports sending PINGs, they are not automatic: the user 33 | needs to invoke 34 | .BR ws_ping() 35 | periodically, whether in a separate thread (recommended) or not. 36 | 37 | The interval between each call determines the 'timeout' between PINGs. 38 | 39 | Threshold must be positive and greater than zero, and determines how many 40 | PINGs can be ignored. 41 | .SH RETURN VALUE 42 | The function does not return any value. 43 | .SH AUTHOR 44 | Davidson Francis (davidsondfgl@gmail.com) 45 | -------------------------------------------------------------------------------- /doc/man/man3/ws_sendframe.3: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (C) 2016-2023 Davidson Francis 3 | .\" 4 | .\" This program is free software: you can redistribute it and/or modify 5 | .\" it under the terms of the GNU General Public License as published by 6 | .\" the Free Software Foundation, either version 3 of the License, or 7 | .\" (at your option) any later version. 8 | .\" 9 | .\" This program is distributed in the hope that it will be useful, 10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | .\" GNU General Public License for more details. 13 | .\" 14 | .\" You should have received a copy of the GNU General Public License 15 | .\" along with this program. If not, see 16 | .\" 17 | .TH man 3 "15 Dec 2023" "1.0" "wsServer man page" 18 | .SH NAME 19 | ws_sendframe \- Creates and send a masked WebSocket frame with some payload data. 20 | .SH SYNOPSIS 21 | .nf 22 | .B #include 23 | .sp 24 | .BI "int ws_sendframe(ws_cli_conn_t " client ", const char " *msg ", ssize_t " size ", int " type "); 25 | .fi 26 | .SH DESCRIPTION 27 | .BR ws_sendframe () 28 | sends a given message 29 | .I msg 30 | to the client 31 | .I client 32 | with the size 33 | .I size 34 | for a given frame 35 | .I type. 36 | This routine is intended to be used to create a websocket frame for 37 | a given type and sending to the client. For higher level routines, 38 | please check 39 | .I ws_sendframe_txt 40 | and 41 | .I ws_sendframe_bin. 42 | .SH RETURN VALUE 43 | Returns the number of bytes written. 44 | .SH NOTES 45 | .PP 46 | The parameter 47 | .I size 48 | can be any value or -1, if the later, the size will be automatically calculated 49 | from the message. Please note that null terminated strings are expected. 50 | .PP 51 | The acceptable values for 52 | .I type 53 | are: 54 | .BR WS_FR_OP_TXT, 55 | .BR WS_FR_OP_BIN 56 | and 57 | .BR WS_FR_OP_PONG. 58 | .SH SEE ALSO 59 | .BR ws_sendframe_bcast (3), 60 | .BR ws_sendframe_txt (3), 61 | .BR ws_sendframe_txt_bcast (3), 62 | .BR ws_sendframe_bin (3), 63 | .BR ws_sendframe_bin_bcast (3) 64 | .SH AUTHOR 65 | Davidson Francis (davidsondfgl@gmail.com) 66 | -------------------------------------------------------------------------------- /doc/man/man3/ws_sendframe_bcast.3: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (C) 2016-2023 Davidson Francis 3 | .\" 4 | .\" This program is free software: you can redistribute it and/or modify 5 | .\" it under the terms of the GNU General Public License as published by 6 | .\" the Free Software Foundation, either version 3 of the License, or 7 | .\" (at your option) any later version. 8 | .\" 9 | .\" This program is distributed in the hope that it will be useful, 10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | .\" GNU General Public License for more details. 13 | .\" 14 | .\" You should have received a copy of the GNU General Public License 15 | .\" along with this program. If not, see 16 | .\" 17 | .TH man 3 "15 Dec 2023" "1.0" "wsServer man page" 18 | .SH NAME 19 | ws_sendframe \- Creates and send a masked WebSocket frame with some payload data. 20 | .SH SYNOPSIS 21 | .nf 22 | .B #include 23 | .sp 24 | .BI "int ws_sendframe_bcast(uint16_t " port ", const char " *msg ", ssize_t " size ", int " type "); 25 | .fi 26 | .SH DESCRIPTION 27 | .BR ws_sendframe_bcast () 28 | broadcasts a given message 29 | .I msg 30 | to all clients connected to the 31 | .I port 32 | with the size 33 | .I size 34 | for a given frame 35 | .I type. 36 | This routine is intended to be used to create a websocket frame for 37 | a given type and broadcasting them. For higher level routines, 38 | please check 39 | .I ws_sendframe_txt() 40 | and 41 | .I ws_sendframe_bin() 42 | .SH RETURN VALUE 43 | Returns the number of bytes written. 44 | .SH NOTES 45 | .PP 46 | The parameter 47 | .I size 48 | can be any value or -1, if the later, the size will be automatically calculated 49 | from the message. Please note that null terminated strings are expected. 50 | .PP 51 | The acceptable values for 52 | .I type 53 | are: 54 | .BR WS_FR_OP_TXT, 55 | .BR WS_FR_OP_BIN 56 | and 57 | .BR WS_FR_OP_PONG. 58 | .SH SEE ALSO 59 | .BR ws_sendframe_bcast (3), 60 | .BR ws_sendframe_txt (3), 61 | .BR ws_sendframe_txt_bcast (3), 62 | .BR ws_sendframe_bin (3), 63 | .BR ws_sendframe_bin_bcast (3) 64 | .SH AUTHOR 65 | Davidson Francis (davidsondfgl@gmail.com) 66 | -------------------------------------------------------------------------------- /doc/man/man3/ws_sendframe_bin.3: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (C) 2016-2023 Davidson Francis 3 | .\" 4 | .\" This program is free software: you can redistribute it and/or modify 5 | .\" it under the terms of the GNU General Public License as published by 6 | .\" the Free Software Foundation, either version 3 of the License, or 7 | .\" (at your option) any later version. 8 | .\" 9 | .\" This program is distributed in the hope that it will be useful, 10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | .\" GNU General Public License for more details. 13 | .\" 14 | .\" You should have received a copy of the GNU General Public License 15 | .\" along with this program. If not, see 16 | .\" 17 | .TH man 3 "15 Dec 2023" "1.0" "wsServer man page" 18 | .SH NAME 19 | ws_sendframe_bin \- Sends a WebSocket binary frame. 20 | .SH SYNOPSIS 21 | .nf 22 | .B #include 23 | .sp 24 | .BI "int ws_sendframe_bin(ws_cli_conn_t " client ", const char " *msg ", size " size "); 25 | .fi 26 | .SH DESCRIPTION 27 | .BR ws_sendframe_bin () 28 | sends a given text message 29 | .I msg 30 | to the client 31 | .I client 32 | with size 33 | .I size 34 | .SH RETURN VALUE 35 | Returns the number of bytes written. 36 | .SH SEE ALSO 37 | .BR ws_sendframe (3), 38 | .BR ws_sendframe_bcast (3), 39 | .BR ws_sendframe_bin_bcast (3), 40 | .BR ws_sendframe_txt (3), 41 | .BR ws_sendframe_txt_bcast (3) 42 | .SH AUTHOR 43 | Davidson Francis (davidsondfgl@gmail.com) 44 | -------------------------------------------------------------------------------- /doc/man/man3/ws_sendframe_bin_bcast.3: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (C) 2016-2023 Davidson Francis 3 | .\" 4 | .\" This program is free software: you can redistribute it and/or modify 5 | .\" it under the terms of the GNU General Public License as published by 6 | .\" the Free Software Foundation, either version 3 of the License, or 7 | .\" (at your option) any later version. 8 | .\" 9 | .\" This program is distributed in the hope that it will be useful, 10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | .\" GNU General Public License for more details. 13 | .\" 14 | .\" You should have received a copy of the GNU General Public License 15 | .\" along with this program. If not, see 16 | .\" 17 | .TH man 3 "15 Dec 2023" "1.0" "wsServer man page" 18 | .SH NAME 19 | ws_sendframe_bin \- Broadcasts a WebSocket binary frame. 20 | .SH SYNOPSIS 21 | .nf 22 | .B #include 23 | .sp 24 | .BI "int ws_sendframe_bin_bcast(uint16_t " port ", const char " *msg ", size " size "); 25 | .fi 26 | .SH DESCRIPTION 27 | .BR ws_sendframe_bin_bcast () 28 | broadcasts a given text message 29 | .I msg 30 | to all clients connected to the 31 | .I port 32 | with size 33 | .I size 34 | .SH RETURN VALUE 35 | Returns the number of bytes written. 36 | .SH SEE ALSO 37 | .BR ws_sendframe (3), 38 | .BR ws_sendframe_bcast (3), 39 | .BR ws_sendframe_bin_bcast (3), 40 | .BR ws_sendframe_txt (3), 41 | .BR ws_sendframe_txt_bcast (3) 42 | .SH AUTHOR 43 | Davidson Francis (davidsondfgl@gmail.com) 44 | -------------------------------------------------------------------------------- /doc/man/man3/ws_sendframe_txt.3: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (C) 2016-2023 Davidson Francis 3 | .\" 4 | .\" This program is free software: you can redistribute it and/or modify 5 | .\" it under the terms of the GNU General Public License as published by 6 | .\" the Free Software Foundation, either version 3 of the License, or 7 | .\" (at your option) any later version. 8 | .\" 9 | .\" This program is distributed in the hope that it will be useful, 10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | .\" GNU General Public License for more details. 13 | .\" 14 | .\" You should have received a copy of the GNU General Public License 15 | .\" along with this program. If not, see 16 | .\" 17 | .TH man 3 "15 Dec 2023" "1.0" "wsServer man page" 18 | .SH NAME 19 | ws_sendframe_txt \- Sends a WebSocket text frame. 20 | .SH SYNOPSIS 21 | .nf 22 | .B #include 23 | .sp 24 | .BI "int ws_sendframe_txt(ws_cli_conn_t " client ", const char " *msg "); 25 | .fi 26 | .SH DESCRIPTION 27 | .BR ws_sendframe_txt () 28 | sends a given text message 29 | .I msg 30 | to the client 31 | .I client 32 | .SH RETURN VALUE 33 | Returns the number of bytes written. 34 | .SH SEE ALSO 35 | .BR ws_sendframe (3), 36 | .BR ws_sendframe_bcast (3), 37 | .BR ws_sendframe_txt_bcast (3), 38 | .BR ws_sendframe_bin (3), 39 | .BR ws_sendframe_bin_bcast (3), 40 | .SH AUTHOR 41 | Davidson Francis (davidsondfgl@gmail.com) 42 | -------------------------------------------------------------------------------- /doc/man/man3/ws_sendframe_txt_bcast.3: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (C) 2016-2023 Davidson Francis 3 | .\" 4 | .\" This program is free software: you can redistribute it and/or modify 5 | .\" it under the terms of the GNU General Public License as published by 6 | .\" the Free Software Foundation, either version 3 of the License, or 7 | .\" (at your option) any later version. 8 | .\" 9 | .\" This program is distributed in the hope that it will be useful, 10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | .\" GNU General Public License for more details. 13 | .\" 14 | .\" You should have received a copy of the GNU General Public License 15 | .\" along with this program. If not, see 16 | .\" 17 | .TH man 3 "15 Dec 2023" "1.0" "wsServer man page" 18 | .SH NAME 19 | ws_sendframe_txt \- Sends a WebSocket text frame. 20 | .SH SYNOPSIS 21 | .nf 22 | .B #include 23 | .sp 24 | .BI "int ws_sendframe_txt_bcast(uint16_t " port ", const char " *msg "); 25 | .fi 26 | .SH DESCRIPTION 27 | .BR ws_sendframe_txt_bcast () 28 | broadcasts a given text message 29 | .I msg 30 | to all clients connected to the 31 | .I port 32 | .SH RETURN VALUE 33 | Returns the number of bytes written. 34 | .SH SEE ALSO 35 | .BR ws_sendframe (3), 36 | .BR ws_sendframe_bcast (3), 37 | .BR ws_sendframe_txt_bcast (3), 38 | .BR ws_sendframe_bin (3), 39 | .BR ws_sendframe_bin_bcast (3), 40 | .SH AUTHOR 41 | Davidson Francis (davidsondfgl@gmail.com) 42 | -------------------------------------------------------------------------------- /doc/man/man3/ws_socket.3: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (C) 2016-2022 Davidson Francis 3 | .\" 4 | .\" This program is free software: you can redistribute it and/or modify 5 | .\" it under the terms of the GNU General Public License as published by 6 | .\" the Free Software Foundation, either version 3 of the License, or 7 | .\" (at your option) any later version. 8 | .\" 9 | .\" This program is distributed in the hope that it will be useful, 10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | .\" GNU General Public License for more details. 13 | .\" 14 | .\" You should have received a copy of the GNU General Public License 15 | .\" along with this program. If not, see 16 | .\" 17 | .TH man 3 "28 Nov 2023" "1.0" "wsServer man page" 18 | .SH NAME 19 | ws_socket \- Start the WebSocket server 20 | .SH SYNOPSIS 21 | .nf 22 | .B #include 23 | .sp 24 | .BI "int ws_socket(struct ws_server " *ws_srv "); 25 | .fi 26 | .SH DESCRIPTION 27 | .BR ws_socket () 28 | starts the websocket server for the configured parameters 29 | .IR ws_srv , 30 | and events, defined as follows: 31 | 32 | .nf 33 | struct ws_server 34 | { 35 | const char *host; 36 | uint16_t port; 37 | int thread_loop; 38 | uint32_t timeout_ms; 39 | struct ws_events evs; 40 | }; 41 | .fi 42 | 43 | Where: 44 | .RS 2 45 | .IP \(em 2 46 | host - defines the host to be binded, such as "localhost", "0.0.0.0" (for IPv4) 47 | and "::" (for IPv4 and v6) 48 | .PP 49 | .IP \(em 2 50 | port - listening port 51 | .IP \(em 2 52 | thread_loop - specifies if the accept loop should run on the same thread (if 53 | equals 0) or on a different thread (if != 0). In other words, if 54 | .I ws_socket() 55 | should be blocking (value of 0) or non-blocking (value != 0). 56 | .IP \(em 2 57 | evs - event structure (see NOTES) 58 | .PP 59 | 60 | Optionally, a user can set a pre-defined 61 | .I timeout_ms 62 | time (in milliseconds) that each message should have. Think of it like a 'ping': 63 | if the client doesn't respond in x amount of time, the client is unresponsive 64 | and the server shouldn't bother sending message to it. 65 | 66 | In case of doubt, leave as 0. 67 | 68 | .SH RETURN VALUE 69 | Returns 0 if in non-blocking mode. 70 | .SH NOTES 71 | .PP 72 | The structure 73 | .I evs 74 | is defined as follows: 75 | .nf 76 | struct ws_events 77 | { 78 | void (*onopen)(ws_cli_conn_t client); 79 | void (*onclose)(ws_cli_conn_t client); 80 | void (*onmessage)(ws_cli_conn_t client, 81 | const unsigned char * msg, 82 | uint64_t size, int type); 83 | }; 84 | .fi 85 | 86 | Each element corresponds to function pointers that are triggered when the 87 | events occur. 88 | 89 | The events: 90 | .RS 2 91 | .IP \(em 2 92 | onopen: occurs when a client successfully connects and handshakes with the 93 | server. 94 | .IP \(em 2 95 | onclose: occurs when a valid client (that handshaked with the server) 96 | disconnects with the server. The reason is not informed. 97 | .IP \(em 2 98 | onmessage: occurs when a client sends a message (whether txt or bin) to the 99 | server. 100 | .PP 101 | Also note that the thread that sends the events is the same as that deals 102 | with the client connection, so keep in mind that you need to let the 103 | function return. If you want to perform further processing, consider 104 | creating a new thread. 105 | 106 | .SH EXAMPLE 107 | .nf 108 | #include 109 | 110 | void onopen(ws_cli_conn_t client) {} 111 | void onclose(ws_cli_conn_t client) {} 112 | void onmessage(ws_cli_conn_t client) {} 113 | 114 | int main(void) { 115 | ws_socket(&(struct ws_server){ 116 | /* 117 | * Bind host: 118 | * localhost -> localhost/127.0.0.1 119 | * 0.0.0.0 -> global IPv4 120 | * :: -> global IPv4+IPv6 (DualStack) 121 | */ 122 | .host = "localhost", 123 | .port = 8080, 124 | .thread_loop = 0, 125 | .timeout_ms = 1000, 126 | .evs.onopen = &onopen, 127 | .evs.onclose = &onclose, 128 | .evs.onmessage = &onmessage 129 | }); 130 | 131 | /* 132 | * If you want to execute code past ws_socket(), set 133 | * .thread_loop to '1'. 134 | */ 135 | 136 | return (0); 137 | } 138 | 139 | .SH SEE ALSO 140 | .BR ws_sendframe_txt (3), 141 | .BR ws_sendframe_bin (3) 142 | .SH AUTHOR 143 | Davidson Francis (davidsondfgl@gmail.com) 144 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Davidson Francis 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see 15 | 16 | add_subdirectory(echo) 17 | add_subdirectory(ping) 18 | add_subdirectory(vtouchpad) 19 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # wsServer examples 2 | This directory contains usage examples and demos for many different scenarios 3 | that might happen when using wsServer. 4 | 5 | **The examples:** 6 | 7 | - [echo](echo): A simple echo server that broadcasts incoming messages 8 | to all connected clients. 9 | 10 | - [vtouchpad](vtouchpad): A 'virtual touchpad' that remotely controls 11 | a computer's mouse. 12 | 13 | If you have other examples and/or small demos that might be useful for 14 | illustrating wsServer's functionality, feel free to submit a PR. 15 | 16 | ## Building 17 | A typical build with `make` or `cmake` should be able to compile all examples 18 | by default. However, note that some examples/demos may be platform specific 19 | (either Linux, Windows...), so check the specific README for each example in 20 | their respective folders for more information. 21 | -------------------------------------------------------------------------------- /examples/echo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Davidson Francis 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see 15 | 16 | add_executable(echo echo.c) 17 | target_link_libraries(echo ws) 18 | -------------------------------------------------------------------------------- /examples/echo/echo.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016-2023 Davidson Francis 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | /** 25 | * @dir examples/ 26 | * @brief wsServer examples folder 27 | */ 28 | 29 | /* 30 | * @dir examples/echo 31 | * @brief Echo example directory. 32 | * @file echo.c 33 | * @brief Simple echo example. 34 | */ 35 | 36 | /** 37 | * @brief Called when a client connects to the server. 38 | * 39 | * @param client Client connection. The @p client parameter is used 40 | * in order to send messages and retrieve informations about the 41 | * client. 42 | */ 43 | void onopen(ws_cli_conn_t client) 44 | { 45 | char *cli, *port; 46 | cli = ws_getaddress(client); 47 | port = ws_getport(client); 48 | #ifndef DISABLE_VERBOSE 49 | printf("Connection opened, addr: %s, port: %s\n", cli, port); 50 | #endif 51 | } 52 | 53 | /** 54 | * @brief Called when a client disconnects to the server. 55 | * 56 | * @param client Client connection. The @p client parameter is used 57 | * in order to send messages and retrieve informations about the 58 | * client. 59 | */ 60 | void onclose(ws_cli_conn_t client) 61 | { 62 | char *cli; 63 | cli = ws_getaddress(client); 64 | #ifndef DISABLE_VERBOSE 65 | printf("Connection closed, addr: %s\n", cli); 66 | #endif 67 | } 68 | 69 | /** 70 | * @brief Called when a client connects to the server. 71 | * 72 | * @param client Client connection. The @p client parameter is used 73 | * in order to send messages and retrieve informations about the 74 | * client. 75 | * 76 | * @param msg Received message, this message can be a text 77 | * or binary message. 78 | * 79 | * @param size Message size (in bytes). 80 | * 81 | * @param type Message type. 82 | */ 83 | void onmessage(ws_cli_conn_t client, 84 | const unsigned char *msg, uint64_t size, int type) 85 | { 86 | char *cli; 87 | cli = ws_getaddress(client); 88 | #ifndef DISABLE_VERBOSE 89 | printf("I receive a message: %s (size: %" PRId64 ", type: %d), from: %s\n", 90 | msg, size, type, cli); 91 | #endif 92 | 93 | /** 94 | * Mimicks the same frame type received and re-send it again 95 | * 96 | * Please note that we could just use a ws_sendframe_txt() 97 | * or ws_sendframe_bin() here, but we're just being safe 98 | * and re-sending the very same frame type and content 99 | * again. 100 | * 101 | * Alternative functions: 102 | * ws_sendframe() 103 | * ws_sendframe_txt() 104 | * ws_sendframe_txt_bcast() 105 | * ws_sendframe_bin() 106 | * ws_sendframe_bin_bcast() 107 | */ 108 | ws_sendframe_bcast(8080, (char *)msg, size, type); 109 | } 110 | 111 | /** 112 | * @brief Main routine. 113 | * 114 | * @note After invoking @ref ws_socket, this routine never returns, 115 | * unless if invoked from a different thread. 116 | */ 117 | int main(void) 118 | { 119 | ws_socket(&(struct ws_server){ 120 | /* 121 | * Bind host: 122 | * localhost -> localhost/127.0.0.1 123 | * 0.0.0.0 -> global IPv4 124 | * :: -> global IPv4+IPv6 (DualStack) 125 | */ 126 | .host = "0.0.0.0", 127 | .port = 8080, 128 | .thread_loop = 0, 129 | .timeout_ms = 1000, 130 | .evs.onopen = &onopen, 131 | .evs.onclose = &onclose, 132 | .evs.onmessage = &onmessage 133 | }); 134 | 135 | /* 136 | * If you want to execute code past ws_socket(), set 137 | * .thread_loop to '1'. 138 | */ 139 | 140 | return (0); 141 | } 142 | -------------------------------------------------------------------------------- /examples/echo/echo.html: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 111 | 112 | 113 | 114 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /examples/ping/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Davidson Francis 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see 15 | 16 | add_executable(ping ping.c) 17 | target_link_libraries(ping ws) 18 | -------------------------------------------------------------------------------- /examples/ping/ping.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Davidson Francis 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | /** 25 | * @dir examples/ping 26 | * @brief Ping example directory 27 | * 28 | * @file ping.c 29 | * @brief Main file. 30 | */ 31 | 32 | /** 33 | * @brief Called when a client connects to the server. 34 | * 35 | * @param client Client connection. 36 | */ 37 | void onopen(ws_cli_conn_t client) 38 | { 39 | ((void)client); 40 | printf("Connected!\n"); 41 | } 42 | 43 | /** 44 | * @brief Called when a client disconnects to the server. 45 | * 46 | * @param client Client connection. 47 | */ 48 | void onclose(ws_cli_conn_t client) 49 | { 50 | ((void)client); 51 | printf("Disconnected!\n"); 52 | } 53 | 54 | /** 55 | * @brief Called when a client connects to the server. 56 | * 57 | * @param client Client connection. 58 | * 59 | * @param msg Received message. 60 | * @param size Message size (in bytes). 61 | * @param type Message type. 62 | */ 63 | void onmessage(ws_cli_conn_t client, 64 | const unsigned char *msg, uint64_t size, int type) 65 | { 66 | ((void)client); 67 | ((void)msg); 68 | ((void)size); 69 | ((void)type); 70 | } 71 | 72 | /** 73 | * @brief Main routine. 74 | */ 75 | int main(void) 76 | { 77 | ws_socket(&(struct ws_server){ 78 | .host = "0.0.0.0", 79 | .port = 8080, 80 | .thread_loop = 1, 81 | .timeout_ms = 1000, 82 | .evs.onopen = &onopen, 83 | .evs.onclose = &onclose, 84 | .evs.onmessage = &onmessage 85 | }); 86 | 87 | /* 88 | * Periodically send ping frames in the main thread 89 | * and aborts inactive connections. 90 | */ 91 | while (1) 92 | { 93 | /* 94 | * Sends a broadcast PING with 2-DELAY MS of tolerance, i.e: 95 | * the client can miss up to 2 PINGs messages. 96 | * 97 | * The 'timeout' is specified by the time between ws_ping() 98 | * calls. In this example, 10 seconds. 99 | */ 100 | printf("Sending ping...\n"); 101 | ws_ping(0, 2); 102 | 103 | /* Sleep 10 seconds. */ 104 | sleep(10); 105 | } 106 | 107 | return (0); 108 | } 109 | -------------------------------------------------------------------------------- /examples/vtouchpad/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Davidson Francis 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see 15 | 16 | include(CheckIncludeFiles) 17 | 18 | add_executable(vtouchpad vtouchpad.c) 19 | target_compile_definitions(vtouchpad PRIVATE DISABLE_VERBOSE) 20 | 21 | if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD") 22 | target_sources(vtouchpad PRIVATE mouse_x11.c) 23 | 24 | # 25 | # If FreeBSD, add /usr/local/include to the search path too, 26 | # see: https://wiki.freebsd.org/WarnerLosh/UsrLocal 27 | # 28 | if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") 29 | set(CMAKE_REQUIRED_INCLUDES "/usr/local/include;/usr/include") 30 | target_link_directories(vtouchpad PUBLIC /usr/local/lib) 31 | target_include_directories(vtouchpad PUBLIC /usr/local/include) 32 | endif() 33 | 34 | CHECK_INCLUDE_FILES("xdo.h" HAVE_XDO_H) 35 | if (NOT HAVE_XDO_H) 36 | 37 | message(WARNING "libxdo/xdotool not found, trying to use XTest exts...") 38 | CHECK_INCLUDE_FILES("X11/extensions/XTest.h" HAVE_XTEST_H) 39 | 40 | if (NOT HAVE_XTEST_H) 41 | message(WARNING "Unable to find XTest.h, vtouchpad is unable " 42 | "to work in your system, disabling build...") 43 | set_target_properties(vtouchpad 44 | PROPERTIES EXCLUDE_FROM_ALL True) 45 | endif () 46 | 47 | target_link_libraries(vtouchpad X11 Xtst ws pthread) 48 | else() 49 | target_compile_definitions(vtouchpad PUBLIC HAVE_XDOTOOL) 50 | target_link_libraries(vtouchpad X11 xdo ws pthread) 51 | endif() 52 | 53 | elseif (CMAKE_SYSTEM_NAME MATCHES "Windows") 54 | target_sources(vtouchpad PRIVATE mouse_win.c) 55 | target_link_libraries(vtouchpad ws ws2_32) 56 | else() 57 | message(WARNING "Operating System '${CMAKE_SYSTEM_NAME}' not supported!") 58 | set_target_properties(vtouchpad 59 | PROPERTIES EXCLUDE_FROM_ALL True) 60 | endif () 61 | -------------------------------------------------------------------------------- /examples/vtouchpad/README.md: -------------------------------------------------------------------------------- 1 | # vtouchpad 2 | This example implements a simple 'virtual touchpad' that allows a web client to control a remote computer's mouse via wsServer/websocket. 3 | 4 | ## Features 5 |

6 | vtouchpad example 7 |
8 | vtouchpad example 9 |

10 | 11 | The larger area represents the main touchpad, move the cursor over it to move the mouse. Left and right clicks within this area also work as expected. 12 | 13 | The two smaller buttons represent the left and right buttons respectively. 14 | 15 | ## Building 16 | Since mouse movement is OS-dependent, this example works on a limited number of systems, currently Linux, FreeBSD and Windows. 17 | 18 | **Linux and FreeBSD builds:** 19 | 20 | vtouchpad requires the environment to run under X11, and requires libX11, libXtst or libxdo (comes with xdotool). 21 | 22 | A build for Ubuntu and the like: 23 | ```bash 24 | $ sudo apt install libxdo-dev 25 | $ cd wsServer/ 26 | $ mkdir build && cd build/ 27 | $ cmake .. 28 | $ make 29 | $ ./examples/vtouchpad/vtouchpad 30 | ``` 31 | FreeBSD (although xdotool is available, the package is currently without maintainer, so its use is discouraged; a normal build already works as expected): 32 | ```bash 33 | $ cd wsServer/ 34 | $ mkdir build && cd build/ 35 | $ cmake .. 36 | $ make 37 | $ ./examples/vtouchpad/vtouchpad 38 | ``` 39 | 40 | **Windows builds:** 41 | 42 | It does not require any special libraries, and should work on any version above Windows 2000. 43 | 44 | ```bash 45 | $ cd wsServer/ 46 | $ mkdir build && cd build/ 47 | $ cmake .. -G "MinGW Makefiles" 48 | $ mingw32-make 49 | $ ./examples/vtouchpad/vtouchpad.exe 50 | ``` 51 | -------------------------------------------------------------------------------- /examples/vtouchpad/mouse_win.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Davidson Francis 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #if !defined(_WIN32) 24 | #error "Expected Windows here!" 25 | #endif 26 | 27 | #include 28 | #include "vtouchpad.h" 29 | 30 | /** 31 | * @dir examples/vtouchpad 32 | * @brief Touchpad example directory 33 | * 34 | * @file mouse_win.c 35 | * @brief Mouse windows implementation. 36 | */ 37 | 38 | /** 39 | * Stub structure. 40 | */ 41 | struct mouse { int stub; }; 42 | 43 | /** 44 | * @brief Do nothing, stub. 45 | * 46 | * @return Returns always 0. 47 | */ 48 | mouse_t *mouse_new(void) 49 | { 50 | return (NULL); 51 | } 52 | 53 | /** 54 | * @brief Do nothing, stub. 55 | * 56 | * @param mouse Mouse structure pointer. 57 | * 58 | * @return Returns always NULL. 59 | */ 60 | void *mouse_free(mouse_t *mouse) 61 | { 62 | ((void)mouse); 63 | return (NULL); 64 | } 65 | 66 | /** 67 | * @brief Moves the mouse pointed by @p mouse to the 68 | * offsets @p x_off and @p y_off. 69 | * 70 | * @param mouse Mouse structure pointer. 71 | * @param x_off X-coordinate offset. 72 | * @param y_off Y-coordinate offset. 73 | * 74 | * @return Returns 0 if success, 1 otherwise. 75 | */ 76 | int mouse_move_relative(mouse_t *mouse, int x_off, int y_off) 77 | { 78 | ((void)mouse); 79 | int ret; 80 | INPUT input = {0}; 81 | input.type = INPUT_MOUSE; 82 | input.mi.dwFlags = MOUSEEVENTF_MOVE; 83 | input.mi.dx = x_off; 84 | input.mi.dy = y_off; 85 | ret = SendInput(1, &input, sizeof(input)); 86 | return (ret == 0); 87 | } 88 | 89 | /** 90 | * @brief Makes a 'button press' event according to the 91 | * @p mouse pointer and the @p button (left or right). 92 | * 93 | * @param mouse Mouse structure pointer. 94 | * @param button Which button was pressed (either 95 | * MOUSE_BTN_LEFT or MOUSE_BTN_RIGHT). 96 | * 97 | * @return Returns 0 if success, 1 otherwise. 98 | */ 99 | int mouse_down(mouse_t *mouse, int button) 100 | { 101 | ((void)mouse); 102 | int ret; 103 | INPUT input = {0}; 104 | 105 | if (button == MOUSE_BTN_LEFT) 106 | button = MOUSEEVENTF_LEFTDOWN; 107 | else 108 | button = MOUSEEVENTF_RIGHTDOWN; 109 | 110 | input.type = INPUT_MOUSE; 111 | input.mi.dwFlags = button; 112 | ret = SendInput(1, &input, sizeof(input)); 113 | return (ret == 0); 114 | } 115 | 116 | /** 117 | * @brief Makes a 'button release' event according to the 118 | * @p mouse pointer and the @p button (left or right). 119 | * 120 | * @param mouse Mouse structure pointer. 121 | * @param button Which button was released (either 122 | * MOUSE_BTN_LEFT or MOUSE_BTN_RIGHT). 123 | * 124 | * @return Returns 0 if success, 1 otherwise. 125 | */ 126 | int mouse_up(mouse_t *mouse, int button) 127 | { 128 | ((void)mouse); 129 | int ret; 130 | INPUT input = {0}; 131 | 132 | if (button == MOUSE_BTN_LEFT) 133 | button = MOUSEEVENTF_LEFTUP; 134 | else 135 | button = MOUSEEVENTF_RIGHTUP; 136 | 137 | input.type = INPUT_MOUSE; 138 | input.mi.dwFlags = button; 139 | ret = SendInput(1, &input, sizeof(input)); 140 | return (ret == 0); 141 | } 142 | -------------------------------------------------------------------------------- /examples/vtouchpad/mouse_x11.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Davidson Francis 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #if !defined(__linux__) && !defined(__FreeBSD__) 24 | #error "Expected Linux or FreeBSD here!" 25 | #endif 26 | 27 | #include 28 | #ifdef HAVE_XDOTOOL 29 | #include 30 | #else 31 | #include 32 | #endif 33 | 34 | #include "vtouchpad.h" 35 | 36 | /** 37 | * @dir examples/vtouchpad 38 | * @brief Touchpad example directory 39 | * 40 | * @file mouse_x11.c 41 | * @brief Mouse X11 implementation. 42 | */ 43 | 44 | /** 45 | * Note: 46 | * Although the use of libxdo/xdotool seems unnecessary (since libxdo *also* 47 | * uses X11's XTest extension), I believe it can still be interesting: the 48 | * xdotool implementation may change in the future, as well as support new 49 | * systems, so dealing with libxdo seems to be better than using X11 directly. 50 | * 51 | * Link: 52 | * If libxdo present: -lX11 -lxdo 53 | * Else : -lX11 -lXtst 54 | * 55 | */ 56 | 57 | /** 58 | * Mouse pointer, hold OS-dependent data structures. 59 | */ 60 | struct mouse 61 | { 62 | #ifdef HAVE_XDOTOOL 63 | xdo_t *xdo; 64 | #else 65 | Display *dpy; 66 | Window root; 67 | #endif 68 | }; 69 | 70 | /** 71 | * @brief Allocates a new mouse data structure. 72 | * 73 | * @return Returns a new mouse_t pointer object. 74 | */ 75 | mouse_t *mouse_new(void) 76 | { 77 | struct mouse *mouse; 78 | mouse = calloc(1, sizeof(struct mouse)); 79 | if (!mouse) 80 | return (NULL); 81 | #ifdef HAVE_XDOTOOL 82 | mouse->xdo = xdo_new(NULL); 83 | if (!mouse->xdo) 84 | return mouse_free(mouse); 85 | #else 86 | mouse->dpy = XOpenDisplay(0); 87 | if (!mouse->dpy) 88 | return mouse_free(mouse); 89 | mouse->root = XRootWindow(mouse->dpy, DefaultScreen(mouse->dpy)); 90 | #endif 91 | return (mouse); 92 | } 93 | 94 | /** 95 | * @brief Frees a previously allocated mouse_t pointer. 96 | * 97 | * @return Always NULL. 98 | */ 99 | void *mouse_free(mouse_t *mouse) 100 | { 101 | if (!mouse) 102 | goto out; 103 | #ifdef HAVE_XDOTOOL 104 | if (mouse->xdo) 105 | xdo_free(mouse->xdo); 106 | #else 107 | if (mouse->dpy) 108 | XCloseDisplay(mouse->dpy); 109 | #endif 110 | free(mouse); 111 | out: 112 | return (NULL); 113 | } 114 | 115 | /** 116 | * @brief Moves the mouse pointed by @p mouse to the 117 | * offsets @p x_off and @p y_off. 118 | * 119 | * @param mouse Mouse structure pointer. 120 | * @param x_off X-coordinate offset. 121 | * @param y_off Y-coordinate offset. 122 | * 123 | * @return Returns 0 if success, 1 otherwise. 124 | */ 125 | int mouse_move_relative(mouse_t *mouse, int x_off, int y_off) 126 | { 127 | #ifdef HAVE_XDOTOOL 128 | return xdo_move_mouse_relative(mouse->xdo, x_off, y_off); 129 | #else 130 | int ret; 131 | ret = XTestFakeRelativeMotionEvent(mouse->dpy, x_off, y_off, CurrentTime); 132 | XFlush(mouse->dpy); 133 | return (ret == 0); 134 | #endif 135 | } 136 | 137 | /** 138 | * @brief Makes a 'button press' event according to the 139 | * @p mouse pointer and the @p button (left or right). 140 | * 141 | * @param mouse Mouse structure pointer. 142 | * @param button Which button was pressed (either 143 | * MOUSE_BTN_LEFT or MOUSE_BTN_RIGHT). 144 | * 145 | * @return Returns 0 if success, 1 otherwise. 146 | */ 147 | int mouse_down(mouse_t *mouse, int button) 148 | { 149 | #ifdef HAVE_XDOTOOL 150 | return xdo_mouse_down(mouse->xdo, CURRENTWINDOW, button); 151 | #else 152 | int ret; 153 | ret = XTestFakeButtonEvent(mouse->dpy, button, 1, CurrentTime); 154 | XFlush(mouse->dpy); 155 | return (ret == 0); 156 | #endif 157 | } 158 | 159 | /** 160 | * @brief Makes a 'button release' event according to the 161 | * @p mouse pointer and the @p button (left or right). 162 | * 163 | * @param mouse Mouse structure pointer. 164 | * @param button Which button was released (either 165 | * MOUSE_BTN_LEFT or MOUSE_BTN_RIGHT). 166 | * 167 | * @return Returns 0 if success, 1 otherwise. 168 | */ 169 | int mouse_up(mouse_t *mouse, int button) 170 | { 171 | #ifdef HAVE_XDOTOOL 172 | return xdo_mouse_up(mouse->xdo, CURRENTWINDOW, button); 173 | #else 174 | int ret; 175 | ret = XTestFakeButtonEvent(mouse->dpy, button, 0, CurrentTime); 176 | XFlush(mouse->dpy); 177 | return (ret == 0); 178 | #endif 179 | } 180 | -------------------------------------------------------------------------------- /examples/vtouchpad/vtouchpad.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Davidson Francis 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "vtouchpad.h" 25 | 26 | /** 27 | * @dir examples/vtouchpad 28 | * @brief Touchpad example directory 29 | * 30 | * @file vtouchpad.c 31 | * @brief Main file. 32 | */ 33 | 34 | /* Mouse global structure. */ 35 | mouse_t *mouse; 36 | 37 | /** 38 | * @brief Given a string @p ev containing a single event, 39 | * parses it as expected. 40 | * 41 | * The event can be (D stands to 'delimiter', currently ';'): 42 | * (Mouse Movement): mouse_move D off_X D off_Y, where the offset is an 43 | * integer type that represents the amount of pixels 44 | * the mouse have to move. Example: mouse_move;10;-20. 45 | * 46 | * Negative offsets represents movement to the left and 47 | * to the top. Positive offsets are the opposite. 48 | * 49 | * (Mouse Button Press): Represents if a mouse button has been pressed or 50 | * released. 51 | * 52 | * Valid events are: 53 | * a) mouse_btn_left_down (left button press) 54 | * b) mouse_btn_left_up (left button release) 55 | * c) mouse_btn_right_down 56 | * d) mouse_btn_right_up 57 | * 58 | * @param ev String representing the event. 59 | * @param error Set to 1 if an error was found, 0 otherwise. 60 | * 61 | * @returns Returns a mouse_event structure. 62 | */ 63 | struct mouse_event parse_event(const char *ev, int *error) 64 | { 65 | struct mouse_event mev = {0}; 66 | char str[128] = {0}; 67 | char *saveptr, *s; 68 | 69 | *error = 1; 70 | saveptr = NULL; 71 | strncpy(str, ev, sizeof(str) - 1); 72 | 73 | s = strtok_r(str, EVENT_DELIMITER, &saveptr); 74 | if (!strcmp(s, "mouse_move")) 75 | mev.event = MOUSE_MOVE; 76 | else if (!strcmp(s, "mouse_btn_left_down")) 77 | mev.event = MOUSE_BTN_LEFT_DOWN; 78 | else if (!strcmp(s, "mouse_btn_left_up")) 79 | mev.event = MOUSE_BTN_LEFT_UP; 80 | else if (!strcmp(s, "mouse_btn_right_down")) 81 | mev.event = MOUSE_BTN_RIGHT_DOWN; 82 | else if (!strcmp(s, "mouse_btn_right_up")) 83 | mev.event = MOUSE_BTN_RIGHT_UP; 84 | else 85 | { 86 | fprintf(stderr, "Unknown event: (%s)\n", s); 87 | goto out0; 88 | } 89 | 90 | if (mev.event != MOUSE_MOVE) 91 | goto out1; 92 | 93 | /* X offset. */ 94 | s = strtok_r(NULL, EVENT_DELIMITER, &saveptr); 95 | if (!s) 96 | { 97 | fprintf(stderr, "A movement event requires a X/Y offsets, X not found!\n"); 98 | goto out0; 99 | } 100 | mev.x_off = atoi(s); 101 | 102 | /* Y offset. */ 103 | s = strtok_r(NULL, EVENT_DELIMITER, &saveptr); 104 | if (!s) 105 | { 106 | fprintf(stderr, "A movement event requires a X/Y offsets, Y not found!\n"); 107 | goto out0; 108 | } 109 | mev.y_off = atoi(s); 110 | 111 | out1: 112 | *error = 0; 113 | out0: 114 | return (mev); 115 | } 116 | 117 | /** 118 | * @brief On open event, just signals that a new client has 119 | * been connected. 120 | * 121 | * @param client Client connection. 122 | */ 123 | void onopen(ws_cli_conn_t client) { 124 | (void)client; 125 | printf("Connected!\n"); 126 | } 127 | 128 | /** 129 | * @brief On close event, just signals that a client has 130 | * been disconnected. 131 | * 132 | * @param client Client connection. 133 | */ 134 | void onclose(ws_cli_conn_t client) { 135 | (void)client; 136 | printf("Disconnected!\n"); 137 | } 138 | 139 | /** 140 | * @brief For each new event, parses the string as expected all call 141 | * the appropriate routines. 142 | * 143 | * @param client Client connection. (ignored) 144 | * 145 | * @param msg Received message/event. 146 | * 147 | * @param size Message size (in bytes). (ignored) 148 | * 149 | * @param type Message type. (ignored) 150 | */ 151 | void onmessage(ws_cli_conn_t client, 152 | const unsigned char *msg, uint64_t size, int type) 153 | { 154 | ((void)client); 155 | ((void)size); 156 | ((void)type); 157 | 158 | struct mouse_event mev; 159 | int err; 160 | 161 | /* Parse the event. */ 162 | mev = parse_event((const char *)msg, &err); 163 | if (err) 164 | return; 165 | 166 | /* Call the appropriate routine. */ 167 | switch (mev.event) 168 | { 169 | case MOUSE_MOVE: 170 | VTOUCH_DEBUG(("move: %d / %d\n", mev.x_off, mev.y_off)); 171 | mouse_move_relative(mouse, mev.x_off, mev.y_off); 172 | break; 173 | case MOUSE_BTN_LEFT_DOWN: 174 | VTOUCH_DEBUG(("mouse left down\n")); 175 | mouse_down(mouse, MOUSE_BTN_LEFT); 176 | break; 177 | case MOUSE_BTN_LEFT_UP: 178 | VTOUCH_DEBUG(("mouse left up\n")); 179 | mouse_up(mouse, MOUSE_BTN_LEFT); 180 | break; 181 | case MOUSE_BTN_RIGHT_DOWN: 182 | VTOUCH_DEBUG(("mouse right down\n")); 183 | mouse_down(mouse, MOUSE_BTN_RIGHT); 184 | break; 185 | case MOUSE_BTN_RIGHT_UP: 186 | VTOUCH_DEBUG(("mouse right up\n")); 187 | mouse_up(mouse, MOUSE_BTN_RIGHT); 188 | break; 189 | } 190 | } 191 | 192 | /* Main routine. */ 193 | int main(void) 194 | { 195 | /* Mouse. */ 196 | mouse = mouse_new(); 197 | 198 | ws_socket(&(struct ws_server){ 199 | .host = "0.0.0.0", 200 | .port = 8080, 201 | .thread_loop = 0, 202 | .timeout_ms = 1000, 203 | .evs.onopen = &onopen, 204 | .evs.onclose = &onclose, 205 | .evs.onmessage = &onmessage 206 | }); 207 | 208 | mouse_free(mouse); 209 | 210 | return (0); 211 | } 212 | -------------------------------------------------------------------------------- /examples/vtouchpad/vtouchpad.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Davidson Francis 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see 16 | */ 17 | 18 | #ifndef VTOUCHPAD_H 19 | #define VTOUCHPAD_H 20 | 21 | /* Debug. */ 22 | #ifndef DISABLE_VERBOSE 23 | #define VTOUCH_DEBUG(x) printf x 24 | #else 25 | #define VTOUCH_DEBUG(x) 26 | #endif 27 | 28 | /* Mouse definitions. */ 29 | #define MOUSE_MOVE 1 30 | #define MOUSE_BTN_LEFT_DOWN 2 31 | #define MOUSE_BTN_LEFT_UP 4 32 | #define MOUSE_BTN_RIGHT_DOWN 8 33 | #define MOUSE_BTN_RIGHT_UP 16 34 | #define EVENT_DELIMITER "; " 35 | 36 | /* Mouse buttons. */ 37 | #define MOUSE_BTN_LEFT 1 38 | #define MOUSE_BTN_RIGHT 3 39 | 40 | /* Mouse event structure. */ 41 | struct mouse_event 42 | { 43 | int event; 44 | int x_off; 45 | int y_off; 46 | }; 47 | 48 | /* Opaque type to 'struct mouse'. */ 49 | typedef struct mouse mouse_t; 50 | 51 | /* External declarations. */ 52 | extern mouse_t *mouse_new(void); 53 | extern void* mouse_free(mouse_t *mouse); 54 | extern int mouse_move_relative(mouse_t *mouse, int x_off, int y_off); 55 | extern int mouse_down(mouse_t *mouse, int button); 56 | extern int mouse_up(mouse_t *mouse, int button); 57 | 58 | #endif /* MAIN_H */ 59 | -------------------------------------------------------------------------------- /examples/vtouchpad/vtouchpad.html: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 185 | 215 | 216 | 217 | 221 | 222 |
223 |
224 |
225 |
226 | 227 | 228 | -------------------------------------------------------------------------------- /extra/toyws/README.md: -------------------------------------------------------------------------------- 1 | # ToyWS 2 | Since there is some demand to support a client, 'ToyWS' is a response to those 3 | requests: ToyWS is a toy WebSocket client, meaning that it's quite simple and 4 | made to work (guaranteed) only with wsServer. 5 | 6 | Limitations: 7 | - Fixed handshake header 8 | - Fixed frame mask (it should be random) 9 | - No PING/PONG frame support 10 | - No close handshake support: although it can identify CLOSE frames, it 11 | does not send the response, only aborts the connection. 12 | - No support for CONT frames, that is, the entire content of a frame (TXT 13 | or BIN) must be contained within a single frame. 14 | - Possibly other things too. 15 | 16 | Although extremely limited, ToyWS was designed for those who want to _also_ 17 | have a C client that is lightweight and compatible with wsServer, thus, 18 | freeing the need for a browser and/or third-party libraries to test and use 19 | wsServer. 20 | 21 | Maybe this client will evolve into something more complete and general in the 22 | future, but that's not in the roadmap at the moment. 23 | 24 | ## API 25 | The API is quite simple and is summarized in 4 routines, to connect, 26 | disconnect, send and receive frame, as follows: 27 | 28 | ```c 29 | int tws_connect(struct tws_ctx *ctx, const char *ip, uint16_t port); 30 | ``` 31 | Connect to a given `ip` address and `port`. 32 | 33 | **Return**: 34 | Returns a positive number if success, otherwise, a negative number. 35 | 36 | **Note:** 37 | `struct tws_ctx *ctx` is for internal usage and initialized within this 38 | function. There is no need to access this structure or modify its values, ToyWS 39 | just needs it to maintain the consistent client state. 40 | 41 | --- 42 | 43 | ```c 44 | void tws_close(struct tws_ctx *ctx); 45 | ``` 46 | Close the connection for the given `ctx`. 47 | 48 | --- 49 | 50 | ```c 51 | int tws_sendframe(struct tws_ctx *ctx, uint8_t *msg, uint64_t size, int type); 52 | ``` 53 | Send a frame of type `type` with content `msg` and size `size` for a given 54 | context `ctx`. 55 | 56 | Valid frame types are: 57 | - FRM_TXT 58 | - FRM_BIN 59 | 60 | **Return**: 61 | Returns 0 if success, otherwise, a negative number. 62 | 63 | --- 64 | 65 | ```c 66 | int tws_receiveframe(struct tws_ctx *ctx, char **buff, size_t *buff_size, 67 | int *frm_type); 68 | ``` 69 | Receive a frame and save it on `buff`. 70 | 71 | **Parameters:** 72 | 73 | **`buff`:** 74 | 75 | Pointer to the target buffer. If NULL, ToyWS will allocate a new buffer that is 76 | capable to hold the frame and save into `buff`. 77 | 78 | If already exists: the function will try to fill the buffer with the frame 79 | content, if the frame size is bigger than `buff_size`, the function will 80 | reallocate `buff` and update `buff_size` with the new size. 81 | 82 | **`buff_size`:** 83 | 84 | Current buffer size. __Must__ point the a valid memory region. If `*buff` 85 | points to NULL, `*buff_size` must be equals to 0. 86 | 87 | **`frm_type`:** 88 | 89 | Frame type read. The frame type received will be reflected into the contents of 90 | this pointer. 91 | 92 | **Return**: Returns 0 if success, a negative number otherwise. 93 | 94 | **Note**: 95 | 96 | - This routine is blocking, that is, it will only return if it manages to read 97 | a frame or if there is an error during the reading (such as invalid (or 98 | unsupported) frame or server disconnection). 99 | 100 | - At the end of everything, don't forget to free the buffer!. Once its size is 101 | relocated, a single call to 'free' is sufficient. 102 | 103 | ## Example 104 | The example below illustrates the usage (also available at (extra/toyws/tws_test.c)): 105 | ```c 106 | #include 107 | #include 108 | #include 109 | #include "toyws.h" 110 | 111 | int main(void) 112 | { 113 | struct tws_ctx ctx; 114 | char msg[] = "Hello"; 115 | 116 | /* Buffer/frame params. */ 117 | char *buff; 118 | int frm_type; 119 | size_t buff_size; 120 | 121 | buff = NULL; 122 | buff_size = 0; 123 | frm_type = 0; 124 | 125 | if (tws_connect(&ctx, "127.0.0.1", 8080) < 0) 126 | fprintf(stderr, "Unable to connect!\n"); 127 | 128 | /* Send message. */ 129 | printf("Send: %s\n", 130 | (tws_sendframe(&ctx, msg, strlen(msg), FRM_TXT) >= 0 ? 131 | "Success" : "Failed")); 132 | 133 | /* Blocks until receive a single message. */ 134 | if (tws_receiveframe(&ctx, &buff, &buff_size, &frm_type) < 0) 135 | fprintf(stderr, "Unable to receive message!\n"); 136 | 137 | printf("I received: (%s) (type: %s)\n", buff, 138 | (frm_type == FRM_TXT ? "Text" : "Binary")); 139 | 140 | tws_close(&ctx); 141 | 142 | free(buff); 143 | return (0); 144 | } 145 | ``` 146 | -------------------------------------------------------------------------------- /extra/toyws/toyws.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Davidson Francis 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see 16 | */ 17 | 18 | #ifndef TOYWS_H 19 | #define TOYWS_H 20 | 21 | #include 22 | 23 | /* Frame constants. */ 24 | #define FRM_TXT 1 25 | #define FRM_BIN 2 26 | #define FRM_CLSE 8 27 | #define FRM_FIN 128 28 | #define FRM_MSK 128 29 | 30 | #define MESSAGE_LENGTH 1024 31 | 32 | /* Client status. */ 33 | #define TWS_ST_DISCONNECTED 0 34 | #define TWS_ST_CONNECTED 1 35 | 36 | /* Client context. */ 37 | struct tws_ctx 38 | { 39 | uint8_t frm[MESSAGE_LENGTH]; 40 | size_t amt_read; 41 | size_t cur_pos; 42 | int status; 43 | int fd; 44 | }; 45 | 46 | #ifdef __cplusplus 47 | extern "C" { 48 | #endif 49 | 50 | /* External functions. */ 51 | extern int tws_connect(struct tws_ctx *ctx, const char *ip, 52 | uint16_t port); 53 | extern void tws_close(struct tws_ctx *ctx); 54 | extern int tws_sendframe(struct tws_ctx *ctx, uint8_t *msg, 55 | uint64_t size, int type); 56 | extern int tws_receiveframe(struct tws_ctx *ctx, char **buff, 57 | size_t *buff_size, int *frm_type); 58 | 59 | #ifdef __cplusplus 60 | } 61 | #endif 62 | 63 | #endif /* TOYWS_H */ 64 | -------------------------------------------------------------------------------- /extra/toyws/tws_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Davidson Francis 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include "toyws.h" 22 | 23 | int main(void) 24 | { 25 | struct tws_ctx ctx; 26 | char msg[] = "Hello"; 27 | 28 | /* Buffer params. */ 29 | char *buff; 30 | int frm_type; 31 | size_t buff_size; 32 | 33 | buff = NULL; 34 | buff_size = 0; 35 | frm_type = 0; 36 | 37 | if (tws_connect(&ctx, "127.0.0.1", 8080) < 0) 38 | fprintf(stderr, "Unable to connect!\n"); 39 | 40 | /* Send message. */ 41 | printf("Send: %s\n", 42 | (tws_sendframe(&ctx, (uint8_t*)msg, strlen(msg), FRM_TXT) >= 0 ? 43 | "Success" : "Failed")); 44 | 45 | /* Blocks until receive a single message. */ 46 | if (tws_receiveframe(&ctx, &buff, &buff_size, &frm_type) < 0) 47 | fprintf(stderr, "Unable to receive message!\n"); 48 | 49 | printf("I received: (%s) (type: %s)\n", buff, 50 | (frm_type == FRM_TXT ? "Text" : "Binary")); 51 | 52 | tws_close(&ctx); 53 | 54 | free(buff); 55 | return (0); 56 | } 57 | -------------------------------------------------------------------------------- /include/base64.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Base64 encoding/decoding (RFC1341) 3 | * Copyright (c) 2005, Jouni Malinen 4 | * 5 | * This software may be distributed under the terms of the BSD license. 6 | * See README for more details. 7 | */ 8 | 9 | #ifndef BASE64_H 10 | #define BASE64_H 11 | 12 | #include 13 | 14 | unsigned char * base64_encode(const unsigned char *src, size_t len, 15 | size_t *out_len); 16 | unsigned char * base64_decode(const unsigned char *src, size_t len, 17 | size_t *out_len); 18 | 19 | #endif /* BASE64_H */ 20 | -------------------------------------------------------------------------------- /include/sha1.h: -------------------------------------------------------------------------------- 1 | /* 2 | * sha1.h 3 | * 4 | * Description: 5 | * This is the header file for code which implements the Secure 6 | * Hashing Algorithm 1 as defined in FIPS PUB 180-1 published 7 | * April 17, 1995. 8 | * 9 | * Many of the variable names in this code, especially the 10 | * single character names, were used because those were the names 11 | * used in the publication. 12 | * 13 | * Please read the file sha1.c for more information. 14 | * 15 | */ 16 | 17 | #ifndef _SHA1_H_ 18 | #define _SHA1_H_ 19 | 20 | #include 21 | /* 22 | * If you do not have the ISO standard stdint.h header file, then you 23 | * must typdef the following: 24 | * name meaning 25 | * uint32_t unsigned 32 bit integer 26 | * uint8_t unsigned 8 bit integer (i.e., unsigned char) 27 | * int_least16_t integer of >= 16 bits 28 | * 29 | */ 30 | 31 | #ifndef _SHA_enum_ 32 | #define _SHA_enum_ 33 | enum 34 | { 35 | shaSuccess = 0, 36 | shaNull, /* Null pointer parameter */ 37 | shaInputTooLong, /* input data too long */ 38 | shaStateError /* called Input after Result */ 39 | }; 40 | #endif 41 | #define SHA1HashSize 20 42 | 43 | /* 44 | * This structure will hold context information for the SHA-1 45 | * hashing operation 46 | */ 47 | typedef struct SHA1Context 48 | { 49 | uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */ 50 | 51 | uint32_t Length_Low; /* Message length in bits */ 52 | uint32_t Length_High; /* Message length in bits */ 53 | 54 | /* Index into message block array */ 55 | int_least16_t Message_Block_Index; 56 | uint8_t Message_Block[64]; /* 512-bit message blocks */ 57 | 58 | int Computed; /* Is the digest computed? */ 59 | int Corrupted; /* Is the message digest corrupted? */ 60 | } SHA1Context; 61 | 62 | /* 63 | * Function Prototypes 64 | */ 65 | 66 | int SHA1Reset( SHA1Context *); 67 | int SHA1Input( SHA1Context *, 68 | const uint8_t *, 69 | unsigned int); 70 | int SHA1Result( SHA1Context *, 71 | uint8_t Message_Digest[SHA1HashSize]); 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /include/utf8.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2008-2009 Bjoern Hoehrmann 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #ifndef UTF8_DECODE_H 24 | #define UTF8_DECODE_H 25 | 26 | #include 27 | #include 28 | 29 | /* UTF8 return state. */ 30 | #define UTF8_ACCEPT 0 31 | #define UTF8_REJECT 1 32 | 33 | extern int is_utf8(uint8_t* s); 34 | extern int is_utf8_len(uint8_t *s, size_t len); 35 | extern uint32_t is_utf8_len_state(uint8_t *s, size_t len, uint32_t state); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /include/ws.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016-2022 Davidson Francis 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see 16 | */ 17 | 18 | /** 19 | * @dir include/ 20 | * @brief wsServer include directory 21 | * 22 | * @file ws.h 23 | * @brief wsServer constants and functions. 24 | */ 25 | #ifndef WS_H 26 | #define WS_H 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | /** 37 | * @name Global configurations 38 | */ 39 | /**@{*/ 40 | /** 41 | * @brief Max clients connected simultaneously. 42 | */ 43 | #ifndef MAX_CLIENTS 44 | #define MAX_CLIENTS 8 45 | #endif 46 | 47 | /** 48 | * @name Key and message configurations. 49 | */ 50 | /**@{*/ 51 | /** 52 | * @brief Message buffer length. 53 | */ 54 | #define MESSAGE_LENGTH 2048 55 | /** 56 | * @brief Maximum frame/message length. 57 | */ 58 | #define MAX_FRAME_LENGTH (16*1024*1024) 59 | /** 60 | * @brief WebSocket key length. 61 | */ 62 | #define WS_KEY_LEN 24 63 | /** 64 | * @brief Magic string length. 65 | */ 66 | #define WS_MS_LEN 36 67 | /** 68 | * @brief Accept message response length. 69 | */ 70 | #define WS_KEYMS_LEN (WS_KEY_LEN + WS_MS_LEN) 71 | /** 72 | * @brief Magic string. 73 | */ 74 | #define MAGIC_STRING "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" 75 | /**@}*/ 76 | 77 | /** 78 | * @name Handshake constants. 79 | */ 80 | /**@{*/ 81 | /** 82 | * @brief Alias for 'Sec-WebSocket-Key'. 83 | */ 84 | #define WS_HS_REQ "Sec-WebSocket-Key" 85 | 86 | /** 87 | * @brief Handshake accept message length. 88 | */ 89 | #define WS_HS_ACCLEN 130 90 | 91 | /** 92 | * @brief Handshake accept message. 93 | */ 94 | #define WS_HS_ACCEPT \ 95 | "HTTP/1.1 101 Switching Protocols\r\n" \ 96 | "Upgrade: websocket\r\n" \ 97 | "Connection: Upgrade\r\n" \ 98 | "Sec-WebSocket-Accept: " 99 | /**@}*/ 100 | 101 | /** 102 | * @name Frame types. 103 | */ 104 | /**@{*/ 105 | /** 106 | * @brief Frame FIN. 107 | */ 108 | #define WS_FIN 128 109 | 110 | /** 111 | * @brief Frame FIN shift 112 | */ 113 | #define WS_FIN_SHIFT 7 114 | 115 | /** 116 | * @brief Continuation frame. 117 | */ 118 | #define WS_FR_OP_CONT 0 119 | 120 | /** 121 | * @brief Text frame. 122 | */ 123 | #define WS_FR_OP_TXT 1 124 | 125 | /** 126 | * @brief Binary frame. 127 | */ 128 | #define WS_FR_OP_BIN 2 129 | 130 | /** 131 | * @brief Close frame. 132 | */ 133 | #define WS_FR_OP_CLSE 8 134 | 135 | /** 136 | * @brief Ping frame. 137 | */ 138 | #define WS_FR_OP_PING 0x9 139 | 140 | /** 141 | * @brief Pong frame. 142 | */ 143 | #define WS_FR_OP_PONG 0xA 144 | 145 | /** 146 | * @brief Unsupported frame. 147 | */ 148 | #define WS_FR_OP_UNSUPPORTED 0xF 149 | /**@}*/ 150 | 151 | /** 152 | * @name Close codes 153 | */ 154 | /**@{*/ 155 | /** 156 | * @brief Normal close 157 | */ 158 | #define WS_CLSE_NORMAL 1000 159 | /** 160 | * @brief Protocol error 161 | */ 162 | #define WS_CLSE_PROTERR 1002 163 | /**@}*/ 164 | /** 165 | * @brief Inconsistent message (invalid utf-8) 166 | */ 167 | #define WS_CLSE_INVUTF8 1007 168 | 169 | /** 170 | * @name Connection states 171 | */ 172 | /**@{*/ 173 | /** 174 | * @brief Connection not established yet. 175 | */ 176 | #define WS_STATE_CONNECTING 0 177 | /** 178 | * @brief Communicating. 179 | */ 180 | #define WS_STATE_OPEN 1 181 | /** 182 | * @brief Closing state. 183 | */ 184 | #define WS_STATE_CLOSING 2 185 | /** 186 | * @brief Closed. 187 | */ 188 | #define WS_STATE_CLOSED 3 189 | /**@}*/ 190 | 191 | /** 192 | * @name Timeout util 193 | */ 194 | /**@{*/ 195 | /** 196 | * @brief Nanoseconds macro converter 197 | */ 198 | #define MS_TO_NS(x) ((x)*1000000) 199 | /** 200 | * @brief Timeout in milliseconds. 201 | */ 202 | #define TIMEOUT_MS (500) 203 | /**@}*/ 204 | 205 | /** 206 | * @name Handshake constants. 207 | */ 208 | /**@{*/ 209 | /** 210 | * @brief Debug 211 | */ 212 | #ifdef VERBOSE_MODE 213 | #define DEBUG(...) fprintf(stderr, __VA_ARGS__) 214 | #else 215 | #define DEBUG(...) 216 | #endif 217 | /**@}*/ 218 | 219 | #ifndef AFL_FUZZ 220 | #define SEND(client,buf,len) send_all((client), (buf), (len), MSG_NOSIGNAL) 221 | #define RECV(fd,buf,len) recv((fd)->client_sock, (buf), (len), 0) 222 | #else 223 | #define SEND(client,buf,len) write(fileno(stdout), (buf), (len)) 224 | #define RECV(fd,buf,len) read((fd)->client_sock, (buf), (len)) 225 | #endif 226 | 227 | /* Opaque client connection type. */ 228 | typedef uint64_t ws_cli_conn_t; 229 | 230 | /* Opaque server instance type. */ 231 | typedef struct ws_server ws_server_t; 232 | 233 | /** 234 | * @brief Get server context. 235 | * Set when initializing `.context` in `struct ws_server`. 236 | */ 237 | void *ws_get_server_context(ws_cli_conn_t client); 238 | 239 | /** 240 | * @brief Set connection context. 241 | */ 242 | void ws_set_connection_context(ws_cli_conn_t client, void *ptr); 243 | 244 | /** 245 | * @brief Get connection context. 246 | */ 247 | void *ws_get_connection_context(ws_cli_conn_t client); 248 | 249 | /** 250 | * @brief events Web Socket events types. 251 | */ 252 | struct ws_events 253 | { 254 | /** 255 | * @brief On open event, called when a new client connects. 256 | */ 257 | void (*onopen)(ws_cli_conn_t client); 258 | /** 259 | * @brief On close event, called when a client disconnects. 260 | */ 261 | void (*onclose)(ws_cli_conn_t client); 262 | /** 263 | * @brief On message event, called when a client sends a text 264 | * or binary message. 265 | */ 266 | void (*onmessage)(ws_cli_conn_t client, 267 | const unsigned char *msg, uint64_t msg_size, int type); 268 | }; 269 | 270 | /** 271 | * @brief server Web Socket server parameters 272 | */ 273 | struct ws_server 274 | { 275 | /** 276 | * @brief Required hostname that the wsServer will bind to 277 | */ 278 | const char *host; 279 | /** 280 | * @brief Listening port 281 | */ 282 | uint16_t port; 283 | /** 284 | * @brief Whether if the ws_socket() should create a new thread 285 | * and be non-blocking (1) or not (0). 286 | */ 287 | int thread_loop; 288 | /** 289 | * @brief Ping timeout in milliseconds 290 | */ 291 | uint32_t timeout_ms; 292 | /** 293 | * @brief Server events. 294 | */ 295 | struct ws_events evs; 296 | /** 297 | * @brief Server context. 298 | * Provided by the user, can be accessed via `ws_get_server_context` from `onopen`. 299 | */ 300 | void* context; 301 | }; 302 | 303 | /* Forward declarations. */ 304 | 305 | /* Internal usage. */ 306 | extern int get_handshake_accept(char *wsKey, unsigned char **dest); 307 | extern int get_handshake_response(char *hsrequest, char **hsresponse); 308 | 309 | /* External usage. */ 310 | extern char *ws_getaddress(ws_cli_conn_t client); 311 | extern char *ws_getport(ws_cli_conn_t client); 312 | extern int ws_sendframe( 313 | ws_cli_conn_t client, const char *msg, uint64_t size, int type); 314 | extern int ws_sendframe_bcast( 315 | uint16_t port, const char *msg, uint64_t size, int type); 316 | extern int ws_sendframe_txt(ws_cli_conn_t client, const char *msg); 317 | extern int ws_sendframe_txt_bcast(uint16_t port, const char *msg); 318 | extern int ws_sendframe_bin(ws_cli_conn_t client, const char *msg, 319 | uint64_t size); 320 | extern int ws_sendframe_bin_bcast(uint16_t port, const char *msg, 321 | uint64_t size); 322 | extern int ws_get_state(ws_cli_conn_t client); 323 | extern int ws_close_client(ws_cli_conn_t client); 324 | extern int ws_socket(struct ws_server *ws_srv); 325 | 326 | /* Ping routines. */ 327 | extern void ws_ping(ws_cli_conn_t cid, int threshold); 328 | 329 | #ifdef AFL_FUZZ 330 | extern int ws_file(struct ws_events *evs, const char *file); 331 | #endif 332 | 333 | #ifdef __cplusplus 334 | } 335 | #endif 336 | 337 | #endif /* WS_H */ 338 | -------------------------------------------------------------------------------- /src/base64.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Base64 encoding/decoding (RFC1341) 3 | * Copyright (c) 2005-2011, Jouni Malinen 4 | * 5 | * This software may be distributed under the terms of the BSD license. 6 | * See README for more details. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | static const unsigned char base64_table[65] = 15 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 16 | 17 | /** 18 | * base64_encode - Base64 encode 19 | * @src: Data to be encoded 20 | * @len: Length of the data to be encoded 21 | * @out_len: Pointer to output length variable, or %NULL if not used 22 | * Returns: Allocated buffer of out_len bytes of encoded data, 23 | * or %NULL on failure 24 | * 25 | * Caller is responsible for freeing the returned buffer. Returned buffer is 26 | * nul terminated to make it easier to use as a C string. The nul terminator is 27 | * not included in out_len. 28 | */ 29 | unsigned char * base64_encode(const unsigned char *src, size_t len, 30 | size_t *out_len) 31 | { 32 | unsigned char *out, *pos; 33 | const unsigned char *end, *in; 34 | size_t olen; 35 | int line_len; 36 | 37 | olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */ 38 | olen += olen / 72; /* line feeds */ 39 | olen++; /* nul termination */ 40 | if (olen < len) 41 | return NULL; /* integer overflow */ 42 | out = malloc(olen); 43 | if (out == NULL) 44 | return NULL; 45 | 46 | end = src + len; 47 | in = src; 48 | pos = out; 49 | line_len = 0; 50 | while (end - in >= 3) { 51 | *pos++ = base64_table[in[0] >> 2]; 52 | *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; 53 | *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; 54 | *pos++ = base64_table[in[2] & 0x3f]; 55 | in += 3; 56 | line_len += 4; 57 | if (line_len >= 72) { 58 | *pos++ = '\n'; 59 | line_len = 0; 60 | } 61 | } 62 | 63 | if (end - in) { 64 | *pos++ = base64_table[in[0] >> 2]; 65 | if (end - in == 1) { 66 | *pos++ = base64_table[(in[0] & 0x03) << 4]; 67 | *pos++ = '='; 68 | } else { 69 | *pos++ = base64_table[((in[0] & 0x03) << 4) | 70 | (in[1] >> 4)]; 71 | *pos++ = base64_table[(in[1] & 0x0f) << 2]; 72 | } 73 | *pos++ = '='; 74 | line_len += 4; 75 | } 76 | 77 | if (line_len) 78 | *pos++ = '\n'; 79 | 80 | *pos = '\0'; 81 | if (out_len) 82 | *out_len = pos - out; 83 | return out; 84 | } 85 | 86 | 87 | /** 88 | * base64_decode - Base64 decode 89 | * @src: Data to be decoded 90 | * @len: Length of the data to be decoded 91 | * @out_len: Pointer to output length variable 92 | * Returns: Allocated buffer of out_len bytes of decoded data, 93 | * or %NULL on failure 94 | * 95 | * Caller is responsible for freeing the returned buffer. 96 | */ 97 | unsigned char * base64_decode(const unsigned char *src, size_t len, 98 | size_t *out_len) 99 | { 100 | unsigned char dtable[256], *out, *pos, block[4], tmp; 101 | size_t i, count, olen; 102 | int pad = 0; 103 | 104 | memset(dtable, 0x80, 256); 105 | for (i = 0; i < sizeof(base64_table) - 1; i++) 106 | dtable[base64_table[i]] = (unsigned char) i; 107 | dtable['='] = 0; 108 | 109 | count = 0; 110 | for (i = 0; i < len; i++) { 111 | if (dtable[src[i]] != 0x80) 112 | count++; 113 | } 114 | 115 | if (count == 0 || count % 4) 116 | return NULL; 117 | 118 | olen = count / 4 * 3; 119 | pos = out = malloc(olen); 120 | if (out == NULL) 121 | return NULL; 122 | 123 | count = 0; 124 | for (i = 0; i < len; i++) { 125 | tmp = dtable[src[i]]; 126 | if (tmp == 0x80) 127 | continue; 128 | 129 | if (src[i] == '=') 130 | pad++; 131 | block[count] = tmp; 132 | count++; 133 | if (count == 4) { 134 | *pos++ = (block[0] << 2) | (block[1] >> 4); 135 | *pos++ = (block[1] << 4) | (block[2] >> 2); 136 | *pos++ = (block[2] << 6) | block[3]; 137 | count = 0; 138 | if (pad) { 139 | if (pad == 1) 140 | pos--; 141 | else if (pad == 2) 142 | pos -= 2; 143 | else { 144 | /* Invalid padding */ 145 | free(out); 146 | return NULL; 147 | } 148 | break; 149 | } 150 | } 151 | } 152 | 153 | *out_len = pos - out; 154 | return out; 155 | } 156 | -------------------------------------------------------------------------------- /src/handshake.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016-2024 Davidson Francis 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see 16 | */ 17 | #define _POSIX_C_SOURCE 200809L 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | /** 28 | * @dir src/ 29 | * @brief Handshake routines directory 30 | * 31 | * @file handshake.c 32 | * @brief Handshake routines. 33 | */ 34 | 35 | /** 36 | * @brief Gets the field Sec-WebSocket-Accept on response, by 37 | * an previously informed key. 38 | * 39 | * @param wsKey Sec-WebSocket-Key 40 | * @param dest source to be stored the value. 41 | * 42 | * @return Returns 0 if success and a negative number 43 | * otherwise. 44 | * 45 | * @attention This is part of the internal API and is documented just 46 | * for completeness. 47 | */ 48 | int get_handshake_accept(char *wsKey, unsigned char **dest) 49 | { 50 | unsigned char hash[SHA1HashSize]; /* SHA-1 Hash. */ 51 | SHA1Context ctx; /* SHA-1 Context. */ 52 | char *str; /* WebSocket key + magic string. */ 53 | 54 | /* Invalid key. */ 55 | if (!wsKey) 56 | return (-1); 57 | 58 | str = calloc(1, sizeof(char) * (WS_KEY_LEN + WS_MS_LEN + 1)); 59 | if (!str) 60 | return (-1); 61 | 62 | strncpy(str, wsKey, WS_KEY_LEN); 63 | strcat(str, MAGIC_STRING); 64 | 65 | SHA1Reset(&ctx); 66 | SHA1Input(&ctx, (const uint8_t *)str, WS_KEYMS_LEN); 67 | SHA1Result(&ctx, hash); 68 | 69 | *dest = base64_encode(hash, SHA1HashSize, NULL); 70 | *(*dest + strlen((const char *)*dest) - 1) = '\0'; 71 | free(str); 72 | return (0); 73 | } 74 | 75 | /** 76 | * @brief Finds the ocorrence of @p needle in @p haystack, case 77 | * insensitive. 78 | * 79 | * @param haystack Target string to be searched. 80 | * @param needle Substring to search for. 81 | * 82 | * @returns If found, returns a pointer at the beginning of the 83 | * found substring. Otherwise, returns NULL. 84 | */ 85 | static char *strstricase(const char *haystack, const char *needle) 86 | { 87 | size_t length; 88 | for (length = strlen(needle); *haystack; haystack++) 89 | if (!strncasecmp(haystack, needle, length)) 90 | return (char*)haystack; 91 | return (NULL); 92 | } 93 | 94 | /** 95 | * @brief Gets the complete response to accomplish a succesfully 96 | * handshake. 97 | * 98 | * @param hsrequest Client request. 99 | * @param hsresponse Server response. 100 | * 101 | * @return Returns 0 if success and a negative number 102 | * otherwise. 103 | * 104 | * @attention This is part of the internal API and is documented just 105 | * for completeness. 106 | */ 107 | int get_handshake_response(char *hsrequest, char **hsresponse) 108 | { 109 | unsigned char *accept; /* Accept message. */ 110 | char *saveptr; /* strtok_r() pointer. */ 111 | char *s; /* Current string. */ 112 | int ret; /* Return value. */ 113 | 114 | saveptr = NULL; 115 | for (s = strtok_r(hsrequest, "\r\n", &saveptr); s != NULL; 116 | s = strtok_r(NULL, "\r\n", &saveptr)) 117 | { 118 | if (strstricase(s, WS_HS_REQ) != NULL) 119 | break; 120 | } 121 | 122 | /* Ensure that we have a valid pointer. */ 123 | if (s == NULL) 124 | return (-1); 125 | 126 | saveptr = NULL; 127 | s = strtok_r(s, " ", &saveptr); 128 | s = strtok_r(NULL, " ", &saveptr); 129 | 130 | ret = get_handshake_accept(s, &accept); 131 | if (ret < 0) 132 | return (ret); 133 | 134 | *hsresponse = malloc(sizeof(char) * WS_HS_ACCLEN); 135 | if (*hsresponse == NULL) 136 | return (-1); 137 | 138 | strcpy(*hsresponse, WS_HS_ACCEPT); 139 | strcat(*hsresponse, (const char *)accept); 140 | strcat(*hsresponse, "\r\n\r\n"); 141 | 142 | free(accept); 143 | return (0); 144 | } 145 | -------------------------------------------------------------------------------- /src/utf8.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2008-2009 Bjoern Hoehrmann 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | /* 24 | * Amazing utf8 decoder & validator grabbed from: 25 | * http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ 26 | * 27 | * All rights goes to the original author. 28 | */ 29 | 30 | #include "utf8.h" 31 | 32 | static const uint8_t utf8d[] = { 33 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f 34 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f 35 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f 36 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f 37 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f 38 | 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf 39 | 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df 40 | 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef 41 | 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff 42 | 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0 43 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2 44 | 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4 45 | 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6 46 | 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8 47 | }; 48 | 49 | static 50 | uint32_t decode(uint32_t* state, uint32_t* codep, uint32_t byte) { 51 | uint32_t type = utf8d[byte]; 52 | 53 | *codep = (*state != UTF8_ACCEPT) ? 54 | (byte & 0x3fu) | (*codep << 6) : 55 | (0xff >> type) & (byte); 56 | 57 | *state = utf8d[256 + *state*16 + type]; 58 | return *state; 59 | } 60 | 61 | int is_utf8(uint8_t *s) { 62 | uint32_t codepoint, state = 0; 63 | 64 | while (*s) 65 | decode(&state, &codepoint, *s++); 66 | 67 | return state == UTF8_ACCEPT; 68 | } 69 | 70 | int is_utf8_len(uint8_t *s, size_t len) { 71 | uint32_t codepoint, state = 0; 72 | size_t i; 73 | 74 | for (i = 0; i < len; i++) 75 | decode(&state, &codepoint, *s++); 76 | 77 | return state == UTF8_ACCEPT; 78 | } 79 | 80 | uint32_t is_utf8_len_state(uint8_t *s, size_t len, uint32_t state) { 81 | uint32_t codepoint; 82 | size_t i; 83 | 84 | codepoint = 0; 85 | for (i = 0; i < len; i++) 86 | decode(&state, &codepoint, *s++); 87 | 88 | return state; 89 | } 90 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2021 Davidson Francis 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see 15 | 16 | if(ENABLE_WSSERVER_TEST) 17 | 18 | find_program(SHELL sh) 19 | 20 | if(SHELL) 21 | add_test(NAME Autobahn|Testsuite 22 | COMMAND "${SHELL}" "${CMAKE_CURRENT_SOURCE_DIR}/run-autobahn.sh" "CMAKE" 23 | ) 24 | else() 25 | message(FATAL_ERROR "Unable to locate shell") 26 | endif(SHELL) 27 | 28 | endif(ENABLE_WSSERVER_TEST) 29 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2022 Davidson Francis 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see 15 | 16 | WSDIR = $(CURDIR)/../ 17 | EXAMPLE = $(WSDIR)/examples/echo/echo 18 | JSON = $(CURDIR)/wsserver_autobahn/index.json 19 | 20 | # Conflicts 21 | .PHONY: all run_autobahn check_results clean 22 | 23 | # Examples 24 | all: run_autobahn 25 | $(MAKE) check_results 26 | 27 | # Run autobahn 28 | run_autobahn: 29 | @bash run-autobahn.sh 30 | 31 | # Check results 32 | check_results: 33 | @python validate_output.py 34 | 35 | # Clean 36 | clean: 37 | @rm -rf wsserver_autobahn/report 38 | -------------------------------------------------------------------------------- /tests/fuzzy/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 Davidson Francis 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see 15 | 16 | CC = afl-gcc 17 | WSDIR = $(CURDIR)/../../ 18 | INCLUDE = -I $(WSDIR)/include 19 | CFLAGS += -Wall -Wextra -g -DVERBOSE_MODE -DAFL_FUZZ 20 | CFLAGS += $(INCLUDE) -std=c99 -pthread -pedantic 21 | LIB = $(WSDIR)/libws.a 22 | 23 | .PHONY: all run_fuzzy clean 24 | 25 | # Examples 26 | all: ws_file run_fuzzy 27 | 28 | # ws_file 29 | ws_file: ws_file.c $(LIB) 30 | $(CC) $(CFLAGS) $(LDFLAGS) ws_file.c -o ws_file $(LIB) 31 | 32 | # Run fuzzing tests 33 | run_fuzzy: ws_file 34 | @bash run-fuzzy.sh 35 | 36 | # Clean 37 | clean: 38 | @rm -f ws_file 39 | -------------------------------------------------------------------------------- /tests/fuzzy/in/ch_1b_1b_508b_close: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Theldus/wsServer/6941aa88dd5126b3e0fca9029f94dd707c98a816/tests/fuzzy/in/ch_1b_1b_508b_close -------------------------------------------------------------------------------- /tests/fuzzy/in/ch_1b_close: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Theldus/wsServer/6941aa88dd5126b3e0fca9029f94dd707c98a816/tests/fuzzy/in/ch_1b_close -------------------------------------------------------------------------------- /tests/fuzzy/packets/ch_508b_close: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Theldus/wsServer/6941aa88dd5126b3e0fca9029f94dd707c98a816/tests/fuzzy/packets/ch_508b_close -------------------------------------------------------------------------------- /tests/fuzzy/packets/ch_close: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Theldus/wsServer/6941aa88dd5126b3e0fca9029f94dd707c98a816/tests/fuzzy/packets/ch_close -------------------------------------------------------------------------------- /tests/fuzzy/packets/ff_1b_close: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Theldus/wsServer/6941aa88dd5126b3e0fca9029f94dd707c98a816/tests/fuzzy/packets/ff_1b_close -------------------------------------------------------------------------------- /tests/fuzzy/packets/ff_384kB_close: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Theldus/wsServer/6941aa88dd5126b3e0fca9029f94dd707c98a816/tests/fuzzy/packets/ff_384kB_close -------------------------------------------------------------------------------- /tests/fuzzy/packets/ff_ping_ping_close: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Theldus/wsServer/6941aa88dd5126b3e0fca9029f94dd707c98a816/tests/fuzzy/packets/ff_ping_ping_close -------------------------------------------------------------------------------- /tests/fuzzy/packets/frames/close: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Theldus/wsServer/6941aa88dd5126b3e0fca9029f94dd707c98a816/tests/fuzzy/packets/frames/close -------------------------------------------------------------------------------- /tests/fuzzy/packets/frames/ping: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Theldus/wsServer/6941aa88dd5126b3e0fca9029f94dd707c98a816/tests/fuzzy/packets/frames/ping -------------------------------------------------------------------------------- /tests/fuzzy/packets/frames/req_chrome: -------------------------------------------------------------------------------- 1 | GET / HTTP/1.1 2 | Host: 127.0.0.1:8080 3 | Connection: Upgrade 4 | Pragma: no-cache 5 | Cache-Control: no-cache 6 | User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36 7 | Upgrade: websocket 8 | Origin: file:// 9 | Sec-WebSocket-Version: 13 10 | Accept-Encoding: gzip, deflate, br 11 | Accept-Language: pt-BR,pt;q=0.9,en;q=0.8,es;q=0.7,ja;q=0.6 12 | Sec-WebSocket-Key: 6d9YPRqkE/4/sU4/POk7ww== 13 | Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits 14 | 15 | -------------------------------------------------------------------------------- /tests/fuzzy/packets/frames/req_firefox: -------------------------------------------------------------------------------- 1 | GET / HTTP/1.1 2 | Host: 127.0.0.1:8080 3 | User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0 4 | Accept: */* 5 | Accept-Language: en-US,en;q=0.5 6 | Accept-Encoding: gzip, deflate 7 | Sec-WebSocket-Version: 13 8 | Origin: null 9 | Sec-WebSocket-Extensions: permessage-deflate 10 | Sec-WebSocket-Key: j8n3iFH1vFfiTp1xmWAIuw== 11 | Connection: keep-alive, Upgrade 12 | Pragma: no-cache 13 | Cache-Control: no-cache 14 | Upgrade: websocket 15 | 16 | -------------------------------------------------------------------------------- /tests/fuzzy/packets/frames/req_websocat: -------------------------------------------------------------------------------- 1 | GET / HTTP/1.1 2 | Host: 127.0.0.1:8080 3 | Connection: Upgrade 4 | Upgrade: websocket 5 | Sec-WebSocket-Version: 13 6 | Sec-WebSocket-Key: SgZwqWhWxeYDxvVdcUjJIQ== 7 | 8 | -------------------------------------------------------------------------------- /tests/fuzzy/packets/msgs/msg_1byte: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Theldus/wsServer/6941aa88dd5126b3e0fca9029f94dd707c98a816/tests/fuzzy/packets/msgs/msg_1byte -------------------------------------------------------------------------------- /tests/fuzzy/packets/msgs/msg_384kB_cont: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Theldus/wsServer/6941aa88dd5126b3e0fca9029f94dd707c98a816/tests/fuzzy/packets/msgs/msg_384kB_cont -------------------------------------------------------------------------------- /tests/fuzzy/packets/msgs/msg_508bytes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Theldus/wsServer/6941aa88dd5126b3e0fca9029f94dd707c98a816/tests/fuzzy/packets/msgs/msg_508bytes -------------------------------------------------------------------------------- /tests/fuzzy/packets/msgs/msg_98305bytes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Theldus/wsServer/6941aa88dd5126b3e0fca9029f94dd707c98a816/tests/fuzzy/packets/msgs/msg_98305bytes -------------------------------------------------------------------------------- /tests/fuzzy/packets/ws_1b_close: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Theldus/wsServer/6941aa88dd5126b3e0fca9029f94dd707c98a816/tests/fuzzy/packets/ws_1b_close -------------------------------------------------------------------------------- /tests/fuzzy/packets/ws_508b_ping_close: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Theldus/wsServer/6941aa88dd5126b3e0fca9029f94dd707c98a816/tests/fuzzy/packets/ws_508b_ping_close -------------------------------------------------------------------------------- /tests/fuzzy/packets/ws_98305b_close: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Theldus/wsServer/6941aa88dd5126b3e0fca9029f94dd707c98a816/tests/fuzzy/packets/ws_98305b_close -------------------------------------------------------------------------------- /tests/fuzzy/run-fuzzy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Copyright (C) 2016-2020 Davidson Francis 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see 18 | # 19 | 20 | export CURDIR="$(cd "$(dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd)" 21 | export WSDIR="$(readlink -f $CURDIR/../)" 22 | 23 | # AFL Fuzzing 24 | if [ ! -x "$(command -v afl-fuzz)" ] 25 | then 26 | echo "AFL not found! please set afl-fuzz in your PATH" 27 | echo "and AFL_HOME too" 28 | exit 1 29 | fi 30 | 31 | # ws_file should exist 32 | if [ ! -f "$CURDIR/ws_file" ] 33 | then 34 | echo "ws_file not found! please build it first, before" 35 | echo "proceeding!" 36 | exit 1 37 | fi 38 | 39 | echo -e "\n[+] Fuzzing wsServer..." 40 | 41 | # AFL output 42 | if [ -z "$AFL_OUT" ] 43 | then 44 | echo "Please note that is recommended to use it inside a ramdisk..." 45 | echo "You can set an output folder with: AFL_OUT env var" 46 | AFL_OUT=$CURDIR/out 47 | fi 48 | 49 | echo -e " -> Output dir: ($AFL_OUT)\n" 50 | afl-fuzz -i "$CURDIR/in" -o "$AFL_OUT" "$CURDIR/ws_file" @@ 51 | -------------------------------------------------------------------------------- /tests/fuzzy/ws_file.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016-2020 Davidson Francis 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #ifndef AFL_FUZZ 25 | #error "This file is intended to be used for fuzzing" 26 | #error "please enable -DAFL_FUZZ on CFLAGS" 27 | #endif 28 | 29 | /** 30 | * @dir tests/ 31 | * @brief wsServer tests folder 32 | * 33 | * @file ws_file.c 34 | * @brief Read a given stream of frames from a file and parse them. 35 | */ 36 | 37 | /** 38 | * @brief Called when a client connects to the server. 39 | * 40 | * @param client Client connection. The @p client parameter is used 41 | * in order to send messages and retrieve informations about the 42 | * client. 43 | */ 44 | void onopen(ws_cli_conn_t client) 45 | { 46 | char *cli; 47 | cli = ws_getaddress(client); 48 | printf("Connection opened, addr: %s\n", cli); 49 | } 50 | 51 | /** 52 | * @brief Called when a client disconnects to the server. 53 | * 54 | * @param client Client connection. The @p client parameter is used 55 | * in order to send messages and retrieve informations about the 56 | * client. 57 | */ 58 | void onclose(ws_cli_conn_t client) 59 | { 60 | char *cli; 61 | cli = ws_getaddress(client); 62 | printf("Connection closed, addr: %s\n", cli); 63 | } 64 | 65 | /** 66 | * @brief Called when a client connects to the server. 67 | * 68 | * @param client Client connection. The @p client parameter is used 69 | * in order to send messages and retrieve informations about the 70 | * client. 71 | * 72 | * @param msg Received message, this message can be a text 73 | * or binary message. 74 | * 75 | * @param size Message size (in bytes). 76 | * 77 | * @param type Message type. 78 | */ 79 | void onmessage(ws_cli_conn_t client, 80 | const unsigned char *msg, uint64_t size, int type) 81 | { 82 | printf("I receive a message: (%.*s) (size: %" PRId64 ", type: %d)\n", 83 | (int)size, msg, size, type); 84 | ws_sendframe(0, (char *)msg, size, type); 85 | } 86 | 87 | /** 88 | * @brief Main routine. 89 | */ 90 | int main(int argc, char **argv) 91 | { 92 | if (argc < 2) 93 | { 94 | fprintf(stderr, "Please: %s \n", argv[0]); 95 | return (-1); 96 | } 97 | 98 | struct ws_events evs; 99 | evs.onopen = &onopen; 100 | evs.onclose = &onclose; 101 | evs.onmessage = &onmessage; 102 | ws_file(&evs, argv[1]); 103 | return (0); 104 | } 105 | -------------------------------------------------------------------------------- /tests/run-autobahn.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright (C) 2016-2021 Davidson Francis 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see 18 | # 19 | 20 | set -e 21 | 22 | # Paths 23 | CURDIR="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" 24 | export CURDIR 25 | 26 | WSDIR="$(readlink -f "$CURDIR"/../)" 27 | export WSDIR 28 | 29 | # Set echo path accordingly with where this 30 | # script was invoked 31 | if [ "$#" -gt 0 ] && [ "$1" = "CMAKE" ] 32 | then 33 | ECHO_BIN="$WSDIR/build/examples/echo/echo" 34 | else 35 | ECHO_BIN="$WSDIR/examples/echo/echo" 36 | fi 37 | 38 | # AFL Fuzzing 39 | if [ -z "$TRAVIS" ] 40 | then 41 | if [ ! -x "$(command -v wstest)" ] 42 | then 43 | echo "Autobahn|Testsuite not found!" 44 | echo "You can install with something like:" 45 | echo " virtualenv ~/wstest" 46 | echo " source ~/wstest/bin/activate" 47 | echo " pip install autobahntestsuite" 48 | exit 1 49 | fi 50 | else 51 | if [ ! -x "$(command -v docker)" ] 52 | then 53 | echo "Docker not found!!" 54 | exit 1 55 | fi 56 | fi 57 | 58 | # echo should exist 59 | if [ ! -f "$ECHO_BIN" ] && [ ! -f "$ECHO_BIN.exe" ] 60 | then 61 | echo "echo not found! please build it first, before" 62 | echo "proceeding!" 63 | exit 1 64 | fi 65 | 66 | printf "\n[+] Running Autobahn...\n" 67 | 68 | # First spawn echo and get its pid 69 | if [ -f "$ECHO_BIN" ] 70 | then 71 | "$ECHO_BIN" & 72 | SR=$! 73 | elif [ -f "$ECHO_BIN.exe" ] 74 | then 75 | wine64-stable "$ECHO_BIN" & 76 | SR=$! 77 | else 78 | echo "Error, echo[.exe] not found!" 79 | exit 1 80 | fi 81 | 82 | # Spawn Autobahn fuzzying client 83 | if [ -z "$TRAVIS" ] 84 | then 85 | cd "$CURDIR" 86 | wstest -m fuzzingclient --spec wsserver_autobahn/fuzzingclient.json 87 | cd - 88 | else 89 | # Run docker image 90 | docker run -t --rm -v \ 91 | "${CURDIR}/wsserver_autobahn:/wsserver_autobahn" \ 92 | theldus/autobahn-testsuite:1.0 93 | fi 94 | 95 | # Kill echo 96 | kill $SR 97 | 98 | # If inside a CMake test, invoke the Python script too 99 | if [ "$#" -gt 0 ] && [ "$1" = "CMAKE" ] 100 | then 101 | cd "$CURDIR" 102 | python validate_output.py || python3 validate_output.py 103 | cd - 104 | fi 105 | -------------------------------------------------------------------------------- /tests/validate_output.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 4 | # Copyright (C) 2016-2021 Davidson Francis 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see 18 | # 19 | 20 | import os 21 | import sys 22 | import json 23 | 24 | # Fancy colors =) 25 | RED="\033[0;31m" 26 | GREEN="\033[0;32m" 27 | NC="\033[0m" 28 | 29 | # Return code 30 | ret = 0 31 | 32 | # Target file 33 | file = "wsserver_autobahn/report/index.json" 34 | 35 | def check_results(): 36 | ret = 0 37 | 38 | # Check if file exists 39 | if not os.path.isfile(file): 40 | sys.stderr.write("{} do not exist!".format(file)) 41 | return 1 42 | 43 | try: 44 | with open(file, "r") as json_file: 45 | json_parsed = json.load(json_file) 46 | except: 47 | sys.stderr.write("Cannot read {}!".format(file)) 48 | return 1 49 | 50 | json_parsed = json_parsed["wsServer"] 51 | 52 | for test in json_parsed: 53 | status = json_parsed[test]["behavior"] 54 | 55 | # We must known that failure, otherwise, we're not passing the tests 56 | # and we must inform the user. 57 | if status == "FAILED": 58 | sys.stderr.write("Test {} was not expected to fail!\n".format(test)) 59 | ret = 1 60 | return ret 61 | 62 | print("[+] Checking output...") 63 | sys.stdout.write("Autobahn|Testsuite tests... ") 64 | 65 | ret = check_results() 66 | 67 | if not ret: 68 | print("[{}PASSED{}]".format(GREEN, NC)) 69 | else: 70 | print("[{}NOT PASSED{}]".format(RED, NC)) 71 | 72 | sys.exit(ret) 73 | -------------------------------------------------------------------------------- /tests/wsserver_autobahn/fuzzingclient.json: -------------------------------------------------------------------------------- 1 | { 2 | "outdir": "./wsserver_autobahn/report", 3 | "servers": [ 4 | { 5 | "agent": "wsServer", "url": "ws://localhost:8080" 6 | } 7 | ], 8 | "cases": ["*"], 9 | "exclude-cases": ["12.*", "13.*"], 10 | "exclude-agent-cases": {} 11 | } 12 | --------------------------------------------------------------------------------