--- /dev/null
+# Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8.3...3.19.1)
+
+CMAKE_POLICY(SET CMP0048 NEW)
+
+SET(fw_name "vine")
+PROJECT(${fw_name})
+SET(PACKAGE_DESCRIPTION "Library and header files for Vine Native API")
+
+SET(VINE_VERSION_MAJOR "1")
+SET(VINE_VERSION_MINOR "0")
+SET(VINE_VERSION_PATCH "0")
+SET(VINE_VERSION ${VINE_VERSION_MAJOR}.${VINE_VERSION_MINOR}.${VINE_VERSION_PATCH})
+
+OPTION(USE_LIBWEBSOCKETS "Use libwebsockets for data path" ON)
+OPTION(USE_LIBWEBSOCKETS_STATIC "Use libwebsockets static library" ON)
+OPTION(USE_LIBWEBSOCKETS_STATIC_PREBUILT "Use prebuilt static library(.a)" ON)
+OPTION(WITH_UNITTEST "With unit tests" OFF)
+OPTION(WITH_VINE_TEST "With vine command-line test tools" OFF)
+OPTION(ENABLE_INSTRUMENTATION_MODE "Enable instrumentation mode" OFF)
+OPTION(ENABLE_DATAPATH_PLUGIN_DEBUG "Enable debug mode for data path plugin" OFF)
+
+INCLUDE(FindPkgConfig)
+
+SET(BUILD_OS "default")
+SET(DEPS "")
+
+FIND_PROGRAM(UNAME NAMES uname)
+EXEC_PROGRAM("${UNAME}" ARGS "-m" OUTPUT_VARIABLE "BUILD_ARCH")
+
+IF(TIZEN_OS)
+ SET(BUILD_OS "tizen")
+ STRING(CONCAT DEPS ${DEPS} "dlog capi-base-common capi-system-info")
+ENDIF(TIZEN_OS)
+
+pkg_check_modules(fw_name_deps REQUIRED ${DEPS})
+
+IF(NOT ANDROID)
+ SET(VINE_DEPS_LIB pthread)
+ENDIF(NOT ANDROID)
+
+FOREACH(flag ${fw_name_deps_CFLAGS})
+ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+IF(ANDROID)
+ SET(BUILD_OS "android")
+ SET(BUILD_ARCH ${ANDROID_ABI})
+ SET(CMAKE_INSTALL_PREFIX ".")
+ENDIF(ANDROID)
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIC -Wall -g")
+SET(CMAKE_C_FLAGS_DEBUG "-O0 -g")
+SET(CMAKE_C_FLAGS_RELEASE "-O2 -g")
+
+# Don't export symbols by default
+ADD_DEFINITIONS("-fvisibility=hidden")
+IF(TIZEN_OS)
+ ADD_DEFINITIONS("-DTIZEN_OS -DUSE_DLOG")
+ENDIF(TIZEN_OS)
+
+IF(ENABLE_INSTRUMENTATION_MODE)
+ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -finstrument-functions -finstrument-functions-exclude-file-list=src/logger")
+ ADD_DEFINITIONS("-DENABLE_INSTRUMENTATION_MODE")
+ENDIF(ENABLE_INSTRUMENTATION_MODE)
+
+SET(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++11")
+SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -Wl,--rpath=${LIBDIR}")
+
+SET(TARGET_VINE "vine")
+SET(VINE_LOGGER "vine-logger")
+SET(VINE_PATH ${PROJECT_SOURCE_DIR}/src)
+SET(VINE_LOGGER_PATH ${PROJECT_SOURCE_DIR}/src/logger)
+
+IF(USE_LIBWEBSOCKETS_STATIC)
+IF(NOT USE_LIBWEBSOCKETS_STATIC_PREBUILT)
+ SET(LWS_WITH_STATIC ON CACHE BOOL "" FORCE)
+ SET(LWS_WITH_SHARED OFF CACHE BOOL "" FORCE)
+ SET(LWS_WITH_EXTERNAL_POLL ON CACHE BOOL "" FORCE)
+ SET(LWS_IPV6 ON CACHE BOOL "" FORCE)
+ SET(LWS_WITHOUT_TESTAPPS ON CACHE BOOL "" FORCE)
+ SET(LWS_WITH_VINE_EXT ON CACHE BOOL "" FORCE)
+ ADD_SUBDIRECTORY(third-party/libwebsockets)
+ENDIF(NOT USE_LIBWEBSOCKETS_STATIC_PREBUILT)
+ SET(PREBUILT_THIRD_PARTY_INCLUDE_PATH "${PROJECT_SOURCE_DIR}/third-party/prebuilt/include")
+ENDIF(USE_LIBWEBSOCKETS_STATIC)
+
+IF(USE_LIBWEBSOCKETS)
+ ADD_DEFINITIONS("-DUSE_LIBWEBSOCKETS")
+ ADD_SUBDIRECTORY(plugins/libwebsockets)
+ENDIF(USE_LIBWEBSOCKETS)
+
+ADD_SUBDIRECTORY(plugins/dns-sd)
+
+ADD_SUBDIRECTORY(include)
+ADD_SUBDIRECTORY(src/logger)
+ADD_SUBDIRECTORY(src)
+ADD_SUBDIRECTORY(pkgconfig)
+ADD_SUBDIRECTORY(tool)
+
+IF(WITH_UNITTEST)
+ADD_SUBDIRECTORY(tests/unittest)
+ENDIF(WITH_UNITTEST)
+
+IF(WITH_VINE_TEST)
+ADD_SUBDIRECTORY(tests/vine-test)
+ADD_SUBDIRECTORY(tests/verifier)
+ENDIF()
--- /dev/null
+Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
--- /dev/null
+# vine
+
+### Preparing to Build (Linux, not Tizen)
+
+#### mDNSresponder
+DNS-SD is used for network service discovery.
+Download mDNresponder tarball in https://opensource.apple.com/tarballs/mDNSResponder/ and install it.
+The recommended version for Vine is 878.70.2.
+
+#### libwebsockets
+libwebsockets is used for providing client and server.
+
+Vine provides a prebuilt static library for libwebsockets by default.
+Please refer to [third-party/prebuilt](./third-party/prebuilt) directory for details.
+
+Otherwise, you can use build libwebsockets manually.
+Download libwebsockets tarball in https://github.com/warmcat/libwebsockets/releases and install it.
+The recommended version for Vine is 3.2.0 or later.
+
+**NOTE**: Vine uses poll() to handle events. So, You have to set LWS_WITH_EXTERNAL_POLL option.
+
+ cmake . -DLWS_WITH_EXTERNAL_POLL=ON
+
+#### googlemock (Optional)
+You can build with unit tests.
+Unit test codes of vine have a dependency on googlemock.
+To build unit tests, you have to prepare the googlemock shared library.
+
+### Build
+You have to tell the build system where libvine.so and vine.h is installed.
+Use -DLIB_DIR for libvine.so and -DINCLUDE_DIR for vine.h
+
+ cmake . -DLIB_DIR=${LIB_DIR} -DINCLUDE_DIR=${INCLUDE_DIR}
+
+Use WITH_VINE_TEST to build with vine test programs.
+You have to tell the build system where the executables (vine-test) is installed.
+Use -DBIN_DIR for the executables.
+
+ cmake . -DLIB_DIR=${LIB_DIR} -DINCLUDE_DIR=${INCLUDE_DIR} -DBIN_DIR=${BIN_DIR} -DWITH_VINE_TEST
+
+Use WITH_UNITTEST to build with unit tests.
+
+ cmake . -DLIB_DIR=${LIB_DIR} -DINCLUDE_DIR=${INCLUDE_DIR} -DWITH_UNITTEST=ON
+
--- /dev/null
+#!/bin/bash
+BUILD_DIR="build"
+
+function clean {
+ echo "Clean build directory"
+ rm -rf $BUILD_DIR
+}
+
+function build {
+ echo "Build vine library"
+ cd $BUILD_DIR
+ cmake .. -DLIB_DIR=/usr/lib -DINCLUDE_DIR=/usr/include -DBIN_DIR=/usr/bin \
+ -DWITH_UNITTEST=ON -DWITH_VINE_TEST=ON $1 $2
+ make
+ sudo make install
+ cd ..
+}
+
+function unittest {
+ echo "Test vine library"
+ ./$BUILD_DIR/tests/unittest/vine-unittest $1
+}
+
+function coding_rule {
+ git clone git@github.sec.samsung.net:RS-SA/coding-style-guides.git
+ chmod 777 ./coding-style-guides/c/*.sh ./coding-style-guides/c/*.pl
+ result_src+="$((`./coding-style-guides/c/checkpatchinit_tizen.sh src| tee /dev/tty | grep total | awk '{ sum+=$2+$4 }; END { print sum }'`))"
+ result_include+="$((`./coding-style-guides/c/checkpatchinit_tizen.sh include| tee /dev/tty | grep total | awk '{ sum+=$2+$4 }; END { print sum }'`))"
+ result_plugins+="$((`./coding-style-guides/c/checkpatchinit_tizen.sh plugins| tee /dev/tty | grep total | awk '{ sum+=$2+$4 }; END { print sum }'`))"
+ result=$(( $result_src + $result_include + $result_plugins ))
+ echo "result $result"
+ if [ "${result}" -gt "0" ]; then
+ exit 1
+ fi
+}
+
+function install_certs {
+ echo "Installs test certificates"
+ cd $BUILD_DIR
+ cp -rf ../tests/vine-test/certs ./tests/vine-test/certs
+ cd tests/vine-test/certs
+ ./create-test-certs.sh
+ cd ../
+}
+
+MINSDKVERSION=24
+
+#NDK_ROOT and NDK_DEP_ROOT_DIR should be defined.
+#NDK_ROOT=/home/moon/Works/Devel/Vine/android-ndk-r21b
+#NDK_DEP_ROOT_DIR=/home/moon/Works/Devel/Vine/ndk-deps
+
+#Libraries that vine depends on should be located in a directory for each ABI.
+#If you want to build for arm64-v8a ABI, libraries should be located in $NDK_DEP_ROOT_DIR/lib/arm64-v8a
+#ABI: armeabi-v7a, arm64-v8a, x86, x86-64
+
+OUTPUT_DIR=artifacts
+
+function build_android {
+ ABI=$1
+ OUTPUT_LIB=$OUTPUT_DIR/lib/$ABI
+ OUTPUT_INC=$OUTPUT_DIR/include
+ OUTPUT_BIN=$OUTPUT_DIR/bin/$ABI
+ NDK_DEP_LIB=$NDK_DEP_ROOT_DIR/lib/$ABI
+ NDK_DEP_INC=$NDK_DEP_ROOT_DIR/include
+
+ echo "Build vine library (ABI=$ABI)"
+ echo "NDK_PATH $NDK_ROOT"
+ cd $BUILD_DIR
+ mkdir -p $OUTPUT_LIB
+ mkdir -p $OUTPUT_INC
+ mkdir -p $OUTPUT_BIN
+ cmake .. \
+ -DCMAKE_TOOLCHAIN_FILE=$NDK_ROOT/build/cmake/android.toolchain.cmake \
+ -DANDROID_ABI=$ABI \
+ -DANDROID_NATIVE_API_LEVEL=$MINSDKVERSION \
+ -DANDROID_STL=c++_static \
+ -DANDROID=ON -DANDROID_NDK=$NDK_ROOT \
+ -DNDK_LIB_DIR=$NDK_DEP_LIB -DNDK_INCLUDE_DIR=$NDK_DEP_INC \
+ -DLIB_DIR=$OUTPUT_LIB -DINCLUDE_DIR=$OUTPUT_INC -DBIN_DIR=$OUTPUT_BIN \
+ -DWITH_UNITTEST=ON -DWITH_VINE_TEST=ON \
+ -DLWS_OPENSSL_INCLUDE_DIRS=$NDK_INCLUDE_DIR \
+ -DLWS_OPENSSL_LIBRARIES="$NDK_DEP_LIB/libcrypto.so;$NDK_DEP_LIB/libssl.so"
+ make
+ make install
+ cd ..
+}
+
+if [ ! -d "$BUILD_DIR" ]; then
+ mkdir -p $BUILD_DIR
+fi
+
+if [ $# -eq 0 ]
+then
+ build
+fi
+
+if [ $# -eq 1 ]
+then
+case $1 in
+ "clean")
+ clean
+ ;;
+ "build")
+ build
+ ;;
+ "test")
+ unittest
+ ;;
+ "test_log")
+ unittest -d
+ ;;
+ "check")
+ coding_rule
+ ;;
+ "debug")
+ build -DENABLE_DATAPATH_PLUGIN_DEBUG=ON -DENABLE_INSTRUMENTATION_MODE=ON
+ ;;
+ "doxygen")
+ doxygen doxygen.conf
+ ;;
+ "install_certs")
+ install_certs
+ ;;
+ "android")
+ build_android arm64-v8a
+ ;;
+esac
+fi
+
+if [ $# -eq 2 ]
+then
+ case $1 in
+ "android")
+ build_android $2
+ ;;
+ esac
+fi
+
--- /dev/null
+# Doxyfile 1.8.13
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = "Vine"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = build_doc
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES += since_tizen="\par Tizen Version:\n"
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 0.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS = 0
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = include/vine.h
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
+
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.cxx \
+ *.cpp \
+ *.c++ \
+ *.java \
+ *.ii \
+ *.ixx \
+ *.ipp \
+ *.i++ \
+ *.inl \
+ *.idl \
+ *.ddl \
+ *.odl \
+ *.h \
+ *.hh \
+ *.hxx \
+ *.hpp \
+ *.h++ \
+ *.cs \
+ *.d \
+ *.php \
+ *.php4 \
+ *.php5 \
+ *.phtml \
+ *.inc \
+ *.m \
+ *.markdown \
+ *.md \
+ *.mm \
+ *.dox \
+ *.py \
+ *.pyw \
+ *.f90 \
+ *.f95 \
+ *.f03 \
+ *.f08 \
+ *.f \
+ *.for \
+ *.tcl \
+ *.vhd \
+ *.vhdl \
+ *.ucf \
+ *.qsf
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse-libclang=ON option for CMake.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sf.net) file that captures the
+# structure of the code including all documentation. Note that this feature is
+# still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: YES.
+
+HAVE_DOT = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = YES
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
+# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
+# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
--- /dev/null
+# Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+INSTALL(FILES
+ ${CMAKE_SOURCE_DIR}/include/vine.h
+ DESTINATION ${INCLUDE_DIR}
+)
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __VINE_H__
+#define __VINE_H__
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdbool.h>
+
+#ifdef TIZEN_OS
+#include <tizen.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file vine.h
+ */
+
+/**
+ * @addtogroup VINE_MODULE
+ * @{
+ */
+
+#ifndef VINE_ERROR
+#define VINE_ERROR -0x03050000
+#endif
+
+/**
+ * @brief Enumeration for Vine error code.
+ * @since_tizen 6.5
+ */
+typedef enum {
+ /**
+ * Successful
+ */
+ VINE_ERROR_NONE = 0,
+ /**
+ * Operation not permitted(1)
+ */
+ VINE_ERROR_NOT_PERMITTED = -EPERM,
+ /**
+ * Out of memory(12)
+ */
+ VINE_ERROR_OUT_OF_MEMORY = -ENOMEM,
+ /**
+ * Permission denied(13)
+ */
+ VINE_ERROR_PERMISSION_DENIED = -EACCES,
+ /**
+ * Invalid function parameter(22)
+ */
+ VINE_ERROR_INVALID_PARAMETER = -EINVAL,
+ /**
+ * Invalid operation(38)
+ */
+ VINE_ERROR_INVALID_OPERATION = -ENOSYS,
+ /**
+ * Connection timed out(110)
+ */
+ VINE_ERROR_CONNECTION_TIME_OUT = -ETIMEDOUT,
+ /**
+ * Now in progress(115)
+ */
+ VINE_ERROR_NOW_IN_PROGRESS = -EINPROGRESS,
+ /**
+ * Not supported
+ */
+ VINE_ERROR_NOT_SUPPORTED,
+ /**
+ * No initialized
+ */
+ VINE_ERROR_NOT_INITIALIZED = VINE_ERROR|0x01,
+ /**
+ * Already initialized
+ */
+ VINE_ERROR_ALREADY_INITIALIZED = VINE_ERROR|0x02,
+ /**
+ * Already enabled
+ */
+ VINE_ERROR_ALREADY_ENABLED = VINE_ERROR|0x03,
+ /**
+ * Operation failed
+ */
+ VINE_ERROR_OPERATION_FAILED = VINE_ERROR|0x04,
+ /**
+ * Service name conflicted
+ */
+ VINE_ERROR_NAME_CONFLICT = VINE_ERROR|0x05,
+ /**
+ * No read data
+ */
+ VINE_ERROR_NO_READ_DATA = VINE_ERROR|0x06,
+ /**
+ * Rejected by peer
+ */
+ //VINE_ERROR_REJECTED_BY_PEER = VINE_ERROR|0x05,
+ /**
+ * Wi-Fi Interface is down
+ */
+ //VINE_ERROR_INTERFACE_DOWN = -ENETDOWN,
+} vine_error_e;
+
+
+/**
+ * @brief Enumeration for data path type
+ * @since_tizen 6.5
+ */
+typedef enum {
+ /**
+ * Server type
+ */
+ VINE_DP_TYPE_SERVER = 0,
+ /**
+ * Client type
+ */
+ VINE_DP_TYPE_CLIENT,
+ /**
+ * Publish-Subscirbe type
+ */
+ VINE_DP_TYPE_PUBSUB,
+ /**
+ * Unknown
+ */
+ VINE_DP_TYPE_UNKNOWN
+} vine_dp_type_e;
+
+/**
+ * @brief Enumeration for security configuration
+ * @since_tizen 6.5
+ */
+typedef enum {
+ /**
+ * No security type
+ */
+ VINE_SECURITY_TYPE_NONE = 0,
+ /**
+ * Uses TLS/SSL for encrypted communication
+ */
+ VINE_SECURITY_TYPE_TLS = 1,
+ /**
+ * Attempts PSK authentication over TLS connection
+ */
+ VINE_SECURITY_TYPE_PSK_OVER_TLS = 2,
+} vine_security_type_e;
+
+/**
+ * @brief Enumeration for TLS version
+ * @since_tizen 6.5
+ */
+typedef enum {
+ /**
+ * Default TLS version
+ */
+ VINE_SECURITY_TLS_VERSION_DEFAULT = 0,
+ /**
+ * TLS 1.0 or later
+ */
+ VINE_SECURITY_TLS_VERSION_1_0 = 100,
+ /**
+ * TLS 1.1 or later
+ */
+ VINE_SECURITY_TLS_VERSION_1_1 = 101,
+ /**
+ * TLS 1.2 or later
+ */
+ VINE_SECURITY_TLS_VERSION_1_2 = 102,
+ /**
+ * TLS 1.3 or later
+ */
+ VINE_SECURITY_TLS_VERSION_1_3 = 103,
+} vine_security_tls_version_e;
+
+/**
+ * @brief Enumeration for certificate verification flag
+ * @since_tizen 6.5
+ */
+typedef enum {
+ /**
+ * Default.
+ */
+ VINE_SECURITY_VERIFICATION_FLAG_DEFAULT = 0,
+ /**
+ * Allows self-signed ceritifcate
+ */
+ VINE_SECURITY_VERIFICATION_FLAG_ALLOW_SELF_SIGNED = (1 << 0),
+ /**
+ * Skips host name(CN) check
+ */
+ VINE_SECURITY_VERIFICATION_FLAG_SKIP_HOST_NAME_CHECK = (1 << 1),
+} vine_security_verification_flag_e;
+
+typedef enum {
+ /**
+ * The discovered service is unavailable
+ */
+ VINE_SERVICE_UNAVAILABLE = 0,
+ /**
+ * The discovered service is available
+ */
+ VINE_SERVICE_AVAILABLE,
+} vine_service_state_e;
+
+/**
+ * @brief Enumeration for capability type.
+ * @since_tizen 6.5
+ */
+typedef enum {
+ /**
+ * The possible discovery methods.
+ */
+ VINE_CAPA_DISCOVERY_METHODS = 0,
+ /**
+ * The max number of connections.
+ */
+ VINE_CAPA_MAX_CONNECTIONS,
+} vine_capability_type_e;
+
+/**
+ * @brief Publish Session
+ * @since_tizen 6.5
+ */
+typedef void *vine_session_h;
+
+/**
+ * @brief Service
+ * @since_tizen 6.5
+ */
+typedef void *vine_service_h;
+
+/**
+ * @brief Data Path
+ * @since_tizen 6.5
+ */
+typedef void *vine_dp_h;
+
+/**
+ * @brief Security configuration
+ * @since_tizen 6.5
+ */
+typedef void *vine_security_h;
+
+/**
+ * @brief Enumeration for service discovery method
+ * @since_tizen 6.5
+ */
+typedef enum {
+ VINE_DISCOVERY_METHOD_DNS_SD = 0,
+} vine_discovery_method_e;
+
+/**
+ * @brief Enumeration for address family
+ * @since_tizen 6.5
+ */
+typedef enum {
+ /*
+ * Default.
+ * IPv6 has a higher priority than IPv4.
+ * For example, IPv6 is used in the dual stack.
+ */
+ VINE_ADDRESS_FAMILY_DEFAULT = 0,
+ /**
+ * IPv4 only.
+ */
+ VINE_ADDRESS_FAMILY_IPV4,
+ /**
+ * IPv6 only.
+ */
+ VINE_ADDRESS_FAMILY_IPV6,
+} vine_address_family_e;
+
+/**
+ * @brief Initializes Vine.
+ * @since_tizen 6.5
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_ALREADY_INITIALIZED Already initialized
+ * @retval #VINE_ERROR_OPERATION_FAILED Operation failed
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_deinitialize()
+ */
+int vine_initialize();
+
+/**
+ * @brief Deinitializes Vine.
+ * @since_tizen 6.5
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_NOT_INITIALIZED Not initialized
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_initialize()
+ */
+int vine_deinitialize();
+
+/**
+ * @brief Gets the capabilities corresponding to @a type.
+ * @since_tizen 6.5
+ * @param[in] type The type of capability
+ * @param[out] capabilities The capabilites
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_NOT_INITIALIZED Not initialized
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_initialize()
+ */
+int vine_get_capabilities(vine_capability_type_e type, int *capabilities);
+
+/**
+ * @brief Gets event file discriptor for monitoring Vine events.
+ * @since_tizen 6.5
+ * @param[in] session The session handle
+ * @param[out] fd The event fd
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ */
+int vine_session_get_event_fd(vine_session_h session, int *fd);
+
+/**
+ * @brief Process occurred event.
+ * @remarks This function should be called in event thread or context.
+ * @since_tizen 6.5
+ * @param[in] session The session handle
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_NOT_INITIALIZED Not initialized
+ * @retval #VINE_ERROR_OPERATION_FAILED Operation failed
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ */
+int vine_session_process_event(vine_session_h session);
+
+/**
+ * @brief Creates a session.
+ * @since_tizen 6.5
+ * @param[out] session The session handle
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_initialize()
+ */
+int vine_session_create(vine_session_h *session);
+
+/**
+ * @brief Destroys a publish session.
+ * @since_tizen 6.5
+ * @param[in] session The session handle
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_initialize()
+ */
+int vine_session_destroy(vine_session_h session);
+
+/**
+ * @brief Called when service is registered.
+ * @since_tizen 6.5
+ * @param[in] session The session handle
+ * @param[in] service_name The registered service name
+ * @param[in] error The result of registration
+ * @param[in] user_data The user data passed from the callback registration function
+ * @see vine_session_set_registered_cb()
+ * @see vine_session_unset_registered_cb()
+ */
+typedef void(*vine_session_registered_cb)(vine_session_h session,
+ const char *service_name, vine_error_e error, void *user_data);
+
+/**
+ * @brief Sets registered callback called when service is registered.
+ * @since_tizen 6.5
+ * @param[in] session The session handle
+ * @param[in] callback The registered callback
+ * @param[in] user_data The user data
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_session_unset_registered_cb()
+ */
+int vine_session_set_registered_cb(vine_session_h session,
+ vine_session_registered_cb callback, void *user_data);
+
+/**
+ * @brief Unsets registered callback.
+ * @since_tizen 6.5
+ * @param[in] session The session handle
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_session_set_registered_cb()
+ */
+int vine_session_unset_registered_cb(vine_session_h session);
+
+/**
+ * @brief Called when service is discovered.
+ * @since_tizen 6.5
+ * @param[in] session The session handle
+ * @param[in] discovered_service The discovered service
+ * @param[in] state The availability of the discovered service
+ * @param[in] user_data The user data passed from the callback registration function
+ * @see vine_session_set_discovered_cb()
+ * @see vine_session_unset_discovered_cb()
+ */
+typedef void(*vine_session_discovered_cb)(vine_session_h session,
+ vine_service_h discovered_service, vine_service_state_e state, void *user_data);
+
+/**
+ * @brief Registers discovered callback called when service is discovered.
+ * @since_tizen 6.5
+ * @param[in] session The session handle
+ * @param[in] callback The discovered callback
+ * @param[in] user_data The user data
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_session_unset_discovered_cb()
+ */
+int vine_session_set_discovered_cb(vine_session_h session,
+ vine_session_discovered_cb callback, void *user_data);
+
+/**
+ * @brief Unregisters discovered callback.
+ * @since_tizen 6.5
+ * @param[in] session The session handle
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_session_set_discovered_cb()
+ */
+int vine_session_unset_discovered_cb(vine_session_h session);
+
+/**
+ * @brief Set the service discovery method
+ * @remarks Default service discovery method is DNS-SD
+ * @since_tizen 6.5
+ * @param[in] session The session handle
+ * @param[in] method The service discovery method
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_session_create()
+ */
+int vine_session_set_discovery_method(vine_session_h session, vine_discovery_method_e method);
+
+/**
+ * @brief Registers a service.
+ * @remarks @a service can be freed after using this API.
+ * @remarks Cannot register if a service is already running.
+ * @remarks If @a iface_name is NULL, all interfaces are used for service registration.
+ * @since_tizen 6.5
+ * @param[in] session The session handle
+ * @param[in] service The service handle
+ * @param[in] iface_name The interface name
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_session_create()
+ * @see vine_session_unregister()
+ */
+int vine_session_register(vine_session_h session,
+ vine_service_h service, const char *iface_name);
+
+/**
+ * @brief Unregisters a service.
+ * @since_tizen 6.5
+ * @param[in] session The session handle
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_session_create()
+ * @see vine_session_register()
+ */
+int vine_session_unregister(vine_session_h session);
+
+/**
+ * @brief Starts a service discovery.
+ * @remarks Cannot discover a service if a service is already running.
+ * @remarks The length of @a service_type should be less than 63 characters. Dot(.) and comma(,) are not allowed.
+ * @remarks If @a iface_name is NULL, all interfaces are used for service registration.
+ * @since_tizen 6.5
+ * @param[in] session The session handle
+ * @param[in] service_type The service_type to be discovered
+ * @param[in] iface_name The interface name
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_session_create()
+ * @see vine_session_stop_discovery()
+ */
+int vine_session_start_discovery(vine_session_h session,
+ const char *service_type, const char *iface_name);
+
+/**
+ * @brief Stops a service discovery
+ * @since_tizen 6.5
+ * @param[in] session The session handle
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_session_create()
+ * @see vine_session_start_discovery()
+ */
+int vine_session_stop_discovery(vine_session_h session);
+
+/**
+ * @brief Called when an IP address is resolved.
+ * @remarks @a ip must be released using free().
+ * @since_tizen 6.5
+ * @param[in] service The service
+ * @param[in] ip The IP address
+ * @param[in] address_family The address familye
+ * @param[in] user_data The user data passed from the callback registration function
+ * @see vine_service_resolve_ip()
+ */
+typedef void(*vine_session_ip_resolved_cb)(vine_session_h session, vine_service_h service,
+ const char *ip, vine_address_family_e address_family, /* availability */
+ void *user_data);
+
+/**
+ * @brief Resolves IP addresses for @a service.
+ * @remarks This API works only for @a service discovered by vine_session_discovered_cb callback
+ * @since_tizen 6.5
+ * @param[in] session The session handle
+ * @param[in] service The service handle
+ * @param[in] callback The resolved callback
+ * @param[in] user_data The user data
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ */
+int vine_session_set_ip_resolved_cb(vine_session_h session,
+ vine_service_h service, vine_session_ip_resolved_cb callback, void *user_data);
+
+/**
+ * @brief Stop resolving IP addresses for @a service.
+ * @remarks This API works only for @a service discovered by vine_session_discovered_cb callback
+ * @since_tizen 6.5
+ * @param[in] session The session handle
+ * @param[in] service The service handle
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ */
+int vine_session_unset_ip_resolved_cb(vine_session_h session,
+ vine_service_h service);
+
+/**
+ * @brief Creates a service.
+ * @remarks @a service should be released using vine_service_destroy().
+ * @since_tizen 6.5
+ * @param[out] service The service handle
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_service_destroy()
+ */
+int vine_service_create(vine_service_h *service);
+
+/**
+ * @brief Destroys a service.
+ * @since_tizen 6.5
+ * @param[in] service The service handle
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_service_create()
+ */
+int vine_service_destroy(vine_service_h service);
+
+/**
+ * @brief Clones a service handle.
+ * @remarks @a cloned should be released using vine_service_destroy().
+ * @since_tizen 6.5
+ * @param[in] origin The origin service handle
+ * @param[out] cloned The cloned service handle
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_service_destroy()
+ */
+int vine_service_clone(vine_service_h origin, vine_service_h *cloned);
+
+/**
+ * @brief Sets the service type for the service. Only for @a service created by vine_service_create().
+ * @remarks For @a service discovered by vine_session_discovered_cb callback, this API doesn't work.
+ * @remarks @a service_type should not be NULL.
+ * @remarks The length of @a service_type should be less than 63 characters. Dot(.) and comma(,) are not allowed.
+ * @remarks @a service_type can be freed after this API is called.
+ * @since_tizen 6.5
+ * @param[in] service The service handle
+ * @param[in] service_type The service type
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_service_create()
+ */
+int vine_service_set_type(vine_service_h service, const char *service_type);
+
+/**
+ * @brief Gets the service type for the service.
+ * @remarks @a service_type must be released using free().
+ * @since_tizen 6.5
+ * @param[in] service The service handle
+ * @param[out] service_type The service type
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_service_create()
+ */
+int vine_service_get_type(vine_service_h service, char **service_type);
+
+/**
+ * @brief Sets the name for the service. Only for @a service created by vine_service_create().
+ * @remarks For @a service discovered by vine_session_discovered_cb callback, this API doesn't work.
+ * @remarks @a service_name should not be NULL.
+ * @remarks The length of @a service_name is limited to 1-63 bytes.
+ * @remarks @a service_name can be freed after this API is called.
+ * @since_tizen 6.5
+ * @param[in] service The service handle
+ * @param[in] service_name The service name
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_service_create()
+ */
+int vine_service_set_name(vine_service_h service, const char *service_name);
+
+/**
+ * @brief Gets the name for the service.
+ * @remarks @a service_name must be released using free().
+ * @since_tizen 6.5
+ * @param[in] service The service handle
+ * @param[out] service_name The service name
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_service_create()
+ */
+int vine_service_get_name(vine_service_h service, char **service_name);
+
+/**
+ * @brief Adds an attribute. Only for @a service created by vine_service_create().
+ * @remarks For @a service discovered by vine_session_discovered_cb callback, this API doesn't work.
+ * @since_tizen 6.5
+ * @remarks @a key is a null-terminated string which is limited to the maximum 9 characters.
+ * @remarks @a key should consist of ASCII values (0x20-0x7E).
+ * @remarks @a value can be NULL, which represents an empty string.
+ * @remarks @a key and @a value will be internally copied, so they can be freed.
+ * @remarks When DNS-SD is used for service discovery, attributes are converted to TXT records.
+ * TXT records generally are limited to less than 100 bytes.
+ * Using TXT records larger than 1300 bytes is NOT RECOMMENDED.
+ * Reference: http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt
+ * The length of an attribute (key length + value length) is limited to 252.
+ * @param[in] service The service handle
+ * @param[in] key The key of attribute
+ * @param[in] value The value of attribute
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_service_create()
+ */
+int vine_service_add_attribute(vine_service_h service,
+ const char *key, const char *value);
+
+/**
+ * @brief Removes an attribute. Only for @a service created by vine_service_create().
+ * @remarks For @a service discovered by vine_session_discovered_cb callback, this API doesn't work.
+ * @since_tizen 6.5
+ * @param[in] service The service handle
+ * @param[in] key The key of attribute
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_service_create()
+ * @see vine_service_add_attribute()
+ */
+int vine_service_remove_attribute(vine_service_h service, const char *key);
+
+/**
+ * @brief Called for each attribute for a service
+ * @remarks @a key and @value are valid only in this function.
+ * @remarks Each key in @a keys is a null-terminated string which is limited to the maximum 9 characters.
+ * @remarks Each key in @a keys consist of ASCII values (0x20-0x7E).
+ * @remarks Each value in @a values can be NULL, which represents an empty string.
+ * @since_tizen 6.5
+ * @param[in] key The key
+ * @param[in] value The value
+ * @param[in] user_data The user data
+ * @return @c true to continue with the next iteration of the loop,
+ * otherwise @c false to break out of the loop.
+ * @see vine_service_foreach_attribute()
+ */
+typedef bool(*vine_service_attribute_cb)(
+ const char *key, const char *value, void *user_data);
+
+/**
+ * @brief Gets the list of attributes. Each attribute is represented as a <key, value> pair.
+ * @since_tizen 6.5
+ * @param[in] service The service handle
+ * @param[in] callback The callback to call with each attribute
+ * @param[in] user_data The user data passed to the callback, or NULL
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_service_create()
+ * @see vine_service_attribute_cb
+ */
+int vine_service_foreach_attribute(vine_service_h service,
+ vine_service_attribute_cb callback, void *user_data);
+
+/**
+ * @brief Sets the port number. Only for @a service created by vine_service_create().
+ * @remarks For @a service discovered by vine_session_discovered_cb callback, this API doesn't work.
+ * @since_tizen 6.5
+ * @param[in] service The service handle
+ * @param[in] port The port number for the service
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_service_create()
+ */
+int vine_service_set_port(vine_service_h service, int port);
+
+/**
+ * @brief Gets the port number.
+ * @remarks @a port will be 0 if it isn't set up.
+ * @since_tizen 6.5
+ * @param[in] service The service handle
+ * @param[out] port The port number
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_service_create()
+ */
+int vine_service_get_port(vine_service_h service, int *port);
+
+/**
+ * @brief Creates the data path handle.
+ * @since_tizen 6.5
+ * @param[in] session The session handle
+ * @param[in] type The data path type
+ * @param[out] dp The data path handle
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_dp_destroy()
+ * @see vine_session_create()
+ */
+int vine_dp_create(vine_session_h session, vine_dp_type_e type, vine_dp_h *dp);
+
+/**
+ * @brief Destroys the data path handle.
+ * @since_tizen 6.5
+ * @param[in] dp The data path handle
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_dp_create()
+ */
+int vine_dp_destroy(vine_dp_h dp);
+
+/**
+ * @brief Sets the interface name to use for datapath.
+ * @remarks If the interface has multiple IP addresses, please set an IP address as @a iface_name.
+ * @since_tizen 6.5
+ * @param[in] dp The data path handle
+ * @param[in] iface_or_ip The interface name or IP address
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_dp_create()
+ */
+int vine_dp_set_iface_name(vine_dp_h dp, const char *iface_or_ip);
+
+/**
+ * @brief Sets the address family.
+ * @remarks This is ignored when dp type is VINE_DP_TYPE_CLIENT.
+ * @since_tizen 6.5
+ * @param[in] dp The data path handle
+ * @param[in] iface_name The interface name
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_dp_create()
+ */
+int vine_dp_set_address_family(vine_dp_h dp, vine_address_family_e addr_family);
+
+/**
+ * @brief Sets the remote IP address.
+ * @since_tizen 6.5
+ * @param[in] dp The data path handle
+ * @param[in] addr_family The address family
+ * @param[in] ip The remote IP address
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_dp_create()
+ */
+int vine_dp_set_remote_ip(vine_dp_h dp, vine_address_family_e addr_family, const char *ip);
+
+/**
+ * @brief Sets the port.
+ * @remarks @a port is a remote server port if @a dp's type is VINE_DP_TYPE_CLIENT. \
+ * Otherwise, @a port is a listen port. \
+ * If it is a listen port, you can set it to 0 so that a random port is picked by the kernel.
+ * @since_tizen 6.5
+ * @param[in] dp The data path handle
+ * @param[in] port The port
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_dp_create()
+ */
+int vine_dp_set_port(vine_dp_h dp, int port);
+
+/**
+ * @brief Gets the port.
+ * @since_tizen 6.5
+ * @param[in] dp The data path handle
+ * @param[out] port The port
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_dp_create()
+ */
+int vine_dp_get_port(vine_dp_h dp, int *port);
+
+/**
+ * @brief Sets the topic. Only for Publish-Subscribe type.
+ * @remarks The length of @a topic should be less than 63 characters.
+ * @since_tizen 6.5
+ * @param[in] dp The data path handle
+ * @param[in] topic The topic
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_dp_create()
+ */
+int vine_dp_set_topic(vine_dp_h dp, const char *topic);
+
+/**
+ * @brief Sets the maximum number of connections.
+ * @remarks This doesn't affect the client dp or the opened dp.
+ * @since_tizen 6.5
+ * @param[in] dp The datapath handle
+ * @param[in] max_conn The maximum number of connections
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_initialize()
+ */
+int vine_dp_set_max_connections(vine_dp_h dp, int max_conn);
+
+/**
+ * @brief Sets the security information.
+ * @remarks Releasing @a security after this function does not affect the operation of the datapath\
+ * because @a security is copied internally.
+ * @since_tizen 6.5
+ * @param[in] dp The data path handle
+ * @param[in] security The security handle
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_dp_create()
+ * @see vine_security_create()
+ */
+int vine_dp_set_security(vine_dp_h dp, vine_security_h security);
+
+/**
+ * @brief Called whenever the peer is accepted.
+ * @remarks @a dp must be released by vine_dp_destroy() if you don't use it anymore.
+ * @since_tizen 6.5
+ * @param[in] dp The data path handle
+ * @param[in] accepted_dp The data path handle for accepted peer
+ * @param[in] user_data The user data passed from the callback registration function
+ * @see vine_dp_set_accepted_cb()
+ */
+typedef void(*vine_dp_accepted_cb)(vine_dp_h dp, vine_dp_h accepted_dp, void *user_data);
+
+/**
+ * @brief Sets accepted callback called when peer is accepted.
+ * @since_tizen 6.5
+ * @param[in] dp The data path handle
+ * @param[in] callback The accepted callback
+ * @param[in] user_data The user data
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_dp_create()
+ * @see vine_dp_unset_accepted_cb()
+ */
+int vine_dp_set_accepted_cb(vine_dp_h dp, vine_dp_accepted_cb callback, void *user_data);
+
+/**
+ * @brief Unsets accepted callback.
+ * @since_tizen 6.5
+ * @param[in] dp The data path handle
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_dp_create()
+ * @see vine_dp_set_accepted_cb()
+ */
+int vine_dp_unset_accepted_cb(vine_dp_h dp);
+
+/**
+ * @brief Called when datapath is terminated by peer.
+ * @since_tizen 6.5
+ * @param[in] dp The terminated datapath
+ * @param[in] user_data The user data passed from the callback registration function
+ * @see vine_dp_set_terminated_cb()
+ * @see vine_dp_unset_terminated_cb()
+ */
+typedef void(*vine_dp_terminated_cb)(vine_dp_h dp, void *user_data);
+
+/**
+ * @brief Registers datapath terminated callback called when datapath is closed by peer.
+ * @since_tizen 6.5
+ * @param[in] dp The datapath handle
+ * @param[in] callback The terminated callback
+ * @param[in] user_data The user data
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_session_unset_data_path_terminated_cb()
+ */
+int vine_dp_set_terminated_cb(vine_dp_h dp, vine_dp_terminated_cb callback, void *user_data);
+
+/**
+ * @brief Unregisters datapath terminated callback.
+ * @since_tizen 6.5
+ * @param[in] dp The datapath handle
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_session_set_data_path_terminated_cb()
+ */
+int vine_dp_unset_terminated_cb(vine_dp_h dp);
+
+/**
+ * @brief Called when @a dp is opened.
+ * @since_tizen 6.5
+ * @param[in] dp The data path handle
+ * @param[in] user_data The user data passed from vine_dp_open()
+ * @see vine_dp_open()
+ */
+typedef void (*vine_dp_opened_cb)(vine_dp_h dp, vine_error_e result, void *user_data);
+
+/**
+ * @brief Opens a datapath.
+ * @remarks This works depending on the type of datapath differently.
+ * If @a dp is a server, a listen port is opened.
+ * If @a dp is a client, it tries to connect to a server.
+ * If @a dp is for Publish-Subscribe, it joins to a service which has the same topic.
+ * @since_tizen 6.5
+ * @param[in] dp The data path handle
+ * @param[in] callback The callback to be called
+ * @param[in] user_data The user data passed to the callback function
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_dp_create()
+ * @see vine_dp_close()
+ */
+int vine_dp_open(vine_dp_h dp, vine_dp_opened_cb callback, void *user_data);
+
+/**
+ * @brief Closes the datapath.
+ * @remarks The network connections with remote peers will be terminated.
+ * @since_tizen 6.5
+ * @param[in] dp The datapath handle
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_dp_open()
+ */
+int vine_dp_close(vine_dp_h dp);
+
+/**
+ * @brief Sends a message through the datapath.
+ * @since_tizen 6.5
+ * @remarks @buf will be sent to a peer when writable state.
+ * @param[in] dp The datapath handle
+ * @param[in] buf The message to be written
+ * @param[in] len The size in bytes to be written
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_dp_create()
+ */
+int vine_dp_send(vine_dp_h dp, unsigned char *buf, size_t len);
+
+/**
+ * @brief Reads up to @buf_len bytes from a @datapath into @buf.
+ * @since_tizen 6.5
+ * @remarks This function can be called in vine_dp_received_cb() only.\
+ * If @buf_len is larger than @received_len of vine_dp_received_cb, \
+ * unexpected data can be read.
+ * @param[in] dp The datapath handle
+ * @param[in] buf The buffer to be filled out
+ * @param[in] buf_len The buffer size
+ * @param[out] read_len The size in bytes to be read
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_dp_create()
+ * @see vine_dp_set_received_cb()
+ */
+int vine_dp_recv(vine_dp_h dp,
+ unsigned char *buf, size_t buf_len, size_t *read_len);
+
+/**
+ * @brief Called when readable state.
+ * @since_tizen 6.5
+ * @remarks vine_dp_recv can be called in this callback
+ * @param[in] dp The datapath handle
+ * @param[in] received_len The number of bytes transferred
+ * @param[in] user_data The user data passed from the callback registration function
+ * @see vine_dp_set_received_cb()
+ * @see vine_dp_unset_received_cb()
+ * @see vine_dp_read()
+ */
+typedef void(*vine_dp_received_cb)(vine_dp_h dp, size_t received_len, void *user_data);
+
+/**
+ * @brief Registers the callback called when a message is received from a peer.
+ * @since_tizen 6.5
+ * @param[in] dp The datapath handle
+ * @param[in] callback The callback function to be called
+ * @param[in] user_data The user data passed to the callback function
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_dp_create()
+ * @see vine_dp_unset_received_cb()
+ */
+int vine_dp_set_received_cb(vine_dp_h dp, vine_dp_received_cb callback, void *user_data);
+
+/**
+ * @brief Unregisters the callback called when a message is received from a peer.
+ * @since_tizen 6.5
+ * @param[in] dp The datapath handle
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_initialize()
+ * @see vine_dp_set_received_cb()
+ */
+int vine_dp_unset_received_cb(vine_dp_h dp);
+
+/**
+ * @brief Creates a security configuration.
+ * @since_tizen 6.5
+ * @param[out] security The security handle
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_security_destroy()
+ */
+int vine_security_create(vine_security_h *security);
+
+/**
+ * @brief Destroys a security configuration.
+ * @since_tizen 6.5
+ * @param[in] security The security handle
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_security_create()
+ */
+int vine_security_destroy(vine_security_h security);
+
+/**
+ * @brief Sets a security type.
+ * @since_tizen 6.5
+ * @param[in] security The security handle
+ * @param[in] type The security type
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_security_get_type()
+ */
+int vine_security_set_type(vine_security_h security, vine_security_type_e type);
+
+/**
+ * @brief Gets a security type.
+ * @since_tizen 6.5
+ * @param[in] security The security handle
+ * @param[out] type The security type
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_security_set_type()
+ */
+int vine_security_get_type(vine_security_h security, vine_security_type_e *type);
+
+/**
+ * @brief Sets TLS version range.
+ * @remarks SSLv2 and SSLv3 are not supported because both versions were proven to be insecure.
+ * @since_tizen 6.5
+ * @param[in] security The security handle
+ * @param[in] version The TLS version
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_security_get_tls_version()
+ * @see vine_security_set_type()
+ */
+int vine_security_set_tls_version(vine_security_h security, vine_security_tls_version_e version);
+
+/**
+ * @brief Gets TLS version range.
+ * @since_tizen 6.5
+ * @param[in] security The security handle
+ * @param[in] version The TLS version
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_security_set_tls_version()
+ * @see vine_security_set_type()
+ */
+int vine_security_get_tls_version(vine_security_h security, vine_security_tls_version_e *version);
+
+/**
+ * @brief Sets combination of verification flags.
+ * @since_tizen 6.5
+ * @param[in] security The security handle
+ * @param[in] flags The bitwise set of one or more of @a vine_security_verification_flag_e
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_security_get_verification_flags()
+ * @see vine_security_set_type()
+ */
+int vine_security_set_verification_flags(vine_security_h security, int flags);
+
+/**
+ * @brief Gets combination of verification flags.
+ * @since_tizen 6.5
+ * @param[in] security The security handle
+ * @param[out] flags The bitwise set of one or more of @a vine_security_verification_flag_e
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_security_set_verification_flags()
+ */
+int vine_security_get_verification_flags(vine_security_h security, int *flags);
+
+/**
+ * @brief Sets CA(Certificate Authority) certificates path.
+ * @remarks The certificates in @ca_path are used when verifying the peer.
+ * @since_tizen 6.5
+ * @param[in] security The security handle
+ * @param[in] ca_path The directory holding CA certificates
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMTER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_security_get_ca_path()
+ */
+int vine_security_set_ca_path(vine_security_h security, const char *ca_path);
+
+/**
+ * @brief Gets CA(Certificate Authority) certificates path.
+ * @remarks @a ca_path must be released using free().
+ * @since_tizen 6.5
+ * @param[in] security The security handle
+ * @param[out] ca_path The directory holding CA certificates
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMTER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_security_set_ca_path()
+ */
+int vine_security_get_ca_path(vine_security_h security, char **ca_path);
+
+/**
+ * @brief Sets certificate path.
+ * @remarks The certificate is transfered to peer during TLS/SSL handshaking.
+ * @since_tizen 6.5
+ * @param[in] security The security handle.
+ * @param[in] cert_path The certificate path.
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMTER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_security_set_type()
+ * @see vine_security_set_private_key()
+ */
+int vine_security_set_cert_path(vine_security_h security, const char *cert_path);
+
+/**
+ * @brief Gets certificate path.
+ * @remarks @a cert_path must be released using free().
+ * @since_tizen 6.5
+ * @param[in] security The security handle.
+ * @param[out] cert_path The certificate path.
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMTER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_security_set_cert_path()
+ */
+int vine_security_get_cert_path(vine_security_h security, char **cert_path);
+
+/**
+ * @brief Sets key path for the certificate.
+ * @since_tizen 6.5
+ * @param[in] security The security handle.
+ * @param[in] key_path The key path.
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_security_set_type()
+ * @see vine_security_set_cert_path()
+ */
+int vine_security_set_private_key(vine_security_h security, const char *key_path);
+
+/**
+ * @brief Gets key path for the certificate.
+ * @remarks @a key_path must be released using free().
+ * @since_tizen 6.5
+ * @param[in] security The security handle.
+ * @param[out] key_path The key path.
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_security_set_private_key()
+ */
+int vine_security_get_private_key(vine_security_h security, char **key_path);
+
+/**
+ * @brief Sets preshared key to verify peer.
+ * @remarks The @psk is exchanged over a websocket connection.
+ * @since_tizen 6.5
+ * @param[in] security The security handle.
+ * @param[in] psk The preshared key
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMTER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_security_get_psk()
+ */
+int vine_security_set_psk(vine_security_h security, const char *psk);
+
+/**
+ * @brief Gets preshared key to verify peer.
+ * @remarks @a psk must be released using free().
+ * @since_tizen 6.5
+ * @param[in] security The security handle.
+ * @param[out] psk The preshared key
+ * @return 0 on success, otherwise a negative error value
+ * @retval #VINE_ERROR_NONE Successful
+ * @retval #VINE_ERROR_INVALID_PARAMTER Invalid parameter
+ * @retval #VINE_ERROR_NOT_SUPPORTED Not supported
+ * @see vine_security_set_psk()
+ */
+int vine_security_get_psk(vine_security_h security, char **psk);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __VINE_H__ */
+
--- /dev/null
+<manifest>\r
+ <request>\r
+ <domain name="_" />\r
+ </request>\r
+</manifest>
\ No newline at end of file
--- /dev/null
+%bcond_without lws_static
+%bcond_without lws_static_prebuilt
+Name: vine
+Summary: An service discovery framework
+Version: 1.0.0
+Release: 0
+Group: Network & Connectivity/API
+License: Apache-2.0
+Source0: %{name}-%{version}.tar.gz
+Source1: %{name}.manifest
+
+BuildRequires: cmake
+BuildRequires: pkgconfig(dns_sd)
+%if %{with lws_static}
+BuildRequires: pkgconfig(openssl1.1)
+%endif
+%if %{without lws_static}
+BuildRequires: pkgconfig(libwebsockets)
+%endif
+
+%if 0%{?_with_tizen:1}
+BuildRequires: pkgconfig(capi-base-common)
+BuildRequires: pkgconfig(capi-system-info)
+BuildRequires: pkgconfig(dlog)
+%endif
+
+BuildRequires: pkgconfig(gmock)
+
+%if 0%{?gcov:1}
+BuildRequires: lcov
+%endif
+
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+
+%description
+A Vine library is a Tizen Framework to provide service discovery and connection to communicate between devices in a local network
+
+%package devel
+Summary: A Vine library in Native API (Development)
+Group: Development/Libraries
+Requires: %{name} = %{version}-%{release}
+Requires: capi-base-common-devel
+
+%description devel
+A Vine library in Native API (Development)
+
+%package test
+Summary: Vine - Test binaries
+
+%description test
+Test Application for Vine
+
+%if 0%{?gcov:1}
+%package gcov
+Summary: Coverage Data of %{name}
+
+%description gcov
+The %{name}-gcov package contains gcov objects
+%endif
+
+%prep
+%setup -q
+chmod 644 %{SOURCE0}
+cp %{SOURCE1} ./%{name}.manifest
+
+%ifarch %{arm}
+export ARCH=arm
+%else
+export ARCH=i586
+%endif
+
+%build
+%if 0%{?gcov:1}
+export CFLAGS+=" -fprofile-arcs -ftest-coverage"
+export CXXFLAGS+=" -fprofile-arcs -ftest-coverage"
+export FFLAGS+=" -fprofile-arcs -ftest-coverage"
+export LDFLAGS+=" -lgcov"
+%endif
+
+%cmake . \
+ -DTIZEN_OS=%{?_with_tizen} \
+ -DCMAKE_BUILD_TYPE=%{?build_type} \
+ -DCMAKE_VERBOSE_MAKEFILE=ON \
+ -DLIB_DIR:PATH=%{_libdir} \
+ -DBIN_DIR:PATH=%{_bindir} \
+ -DINCLUDE_DIR:PATH=%{_includedir} \
+%if %{with lws_static}
+ -DUSE_LIBWEBSOCKETS_STATIC=ON \
+%else
+ -DUSE_LIBWEBSOCKETS_STATIC=OFF \
+%endif
+%if %{with lws_static_prebuilt}
+ -DUSE_LIBWEBSOCKETS_STATIC_PREBUILT=ON \
+%else
+ -DUSE_LIBWEBSOCKETS_STATIC_PREBUILT=OFF \
+%endif
+ -DWITH_UNITTEST=ON \
+ -DWITH_VINE_TEST=ON
+
+make %{?jobs:-j%jobs}
+
+%if 0%{?gcov:1}
+mkdir -p gcov-obj
+find . -name '*.gcno' -exec cp '{}' gcov-obj ';'
+%endif
+
+%install
+rm -rf %{buildroot}
+%make_install
+
+%if 0%{?gcov:1}
+mkdir -p %{buildroot}%{_datadir}/gcov/obj
+install -m 0644 gcov-obj/* %{buildroot}%{_datadir}/gcov/obj
+%endif
+%check
+%if 0%{?gcov:1}
+LD_LIBRARY_PATH=%{buildroot}%{_libdir} ./tests/unittest/vine-unittest
+%endif
+
+%if 0%{?gcov:1}
+lcov -c --ignore-errors graph --no-external -b . -d . -o %{name}.info
+genhtml %{name}.info -o out --legend --show-details
+%endif
+
+%if %{with lws_static}
+rm -rf %{buildroot}%{_libdir}/cmake/libwebsockets
+%endif
+
+%post -p /sbin/ldconfig
+
+%postun -p /sbin/ldconfig
+
+%files
+%manifest %{name}.manifest
+%license LICENSE
+%defattr(-,root,root,-)
+%{_libdir}/*.so*
+
+%files devel
+%defattr(-,root,root,-)
+%{_libdir}/pkgconfig/*.pc
+%{_includedir}/*.h
+%if %{with lws_static} && %{without lws_static_prebuilt}
+%{_includedir}/libwebsockets/*.h
+%{_includedir}/libwebsockets/abstract/*.h
+%{_includedir}/libwebsockets/abstract/protocols/*.h
+%{_includedir}/libwebsockets/abstract/transports/*.h
+%endif
+%{_libdir}/*.so
+
+%files test
+%manifest %{name}.manifest
+%attr(755,root,root) %{_bindir}/*
+%attr(644,root,root) %{_datadir}/vine-verifier/certs/rootCA/ca.pem
+%attr(644,root,root) %{_datadir}/vine-verifier/certs/server/*
+
+%if 0%{?gcov:1}
+%files gcov
+%{_datadir}/gcov/obj/*
+%endif
--- /dev/null
+# Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+SET(PC_NAME ${TARGET_VINE})
+SET(PC_DESCRIPTION ${PACKAGE_DESCRIPTION})
+SET(PC_VERSION ${VINE_VERSION})
+SET(PC_REQUIRED ${DEPS})
+SET(PC_LDFLAGS -l${TARGET_VINE})
+
+CONFIGURE_FILE(${PROJECT_NAME}.pc.in ${PROJECT_NAME}.pc @ONLY)
+
+INSTALL(FILES
+ ${CMAKE_BINARY_DIR}/pkgconfig/${PROJECT_NAME}.pc
+ DESTINATION
+ ${LIB_DIR}/pkgconfig
+ )
--- /dev/null
+# Package Information for pkg-config\r
+\r
+prefix=/usr\r
+exec_prefix=${prefix}\r
+libdir=@LIB_DIR@\r
+includedir=@INCLUDE_DIR@\r
+\r
+Name: @PC_NAME@\r
+Description: @PC_DESCRIPTION@\r
+Version: @PC_VERSION@\r
+Requires: @PC_REQUIRED@\r
+Libs: -L${libdir} @PC_LDFLAGS@\r
+Cflags: -I${includedir}\r
--- /dev/null
+# Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+SET(DNS_SD_PLUGIN "vine-plugin-dns-sd")
+
+SET(DNS_SD_PLUGIN_VERSION_MAJOR "1")
+SET(DNS_SD_PLUGIN_VERSION_MINOR "0")
+SET(DNS_SD_PLUGIN_VERSION_PATCH "0")
+SET(DNS_SD_PLUGIN_VERSION ${DNS_SD_PLUGIN_VERSION_MAJOR}.${DNS_SD_PLUGIN_VERSION_MINOR}.${DNS_SD_PLUGIN_VERSION_PATCH})
+
+INCLUDE_DIRECTORIES(
+ ${VINE_PATH}/include
+ ${VINE_LOGGER_PATH}
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${${fw_name}_INCLUDE_DIRS}
+)
+
+IF(ANDROID)
+ MESSAGE("NDK_INCLUDE_DIR " ${NDK_INCLUDE_DIR})
+ INCLUDE_DIRECTORIES("${NDK_INCLUDE_DIR}")
+ SET(DNS_SD "${NDK_LIB_DIR}/libmdnssd.so")
+ELSE(ANDROID)
+ find_library(
+ DNS_SD
+ NAMES libdns_sd.so
+ HINTS /usr/local/lib /usr/lib /usr/lib64
+ REQUIRED
+ )
+ENDIF(ANDROID)
+
+FILE(GLOB VINE_PLUGIN_SOURCES *.cpp)
+
+ADD_DEFINITIONS("-fvisibility=default")
+ADD_LIBRARY(${DNS_SD_PLUGIN} SHARED ${VINE_PLUGIN_SOURCES})
+
+SET_TARGET_PROPERTIES(
+ ${DNS_SD_PLUGIN}
+ PROPERTIES
+ SOVERSION ${DNS_SD_PLUGIN_VERSION_MAJOR}
+)
+
+TARGET_LINK_LIBRARIES(${DNS_SD_PLUGIN}
+ ${VINE_LOGGER}
+ ${DNS_SD}
+ ${fw_name_deps_LIBRARIES}
+ dl
+)
+
+INSTALL(TARGETS ${DNS_SD_PLUGIN} DESTINATION "${LIB_DIR}")
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+
+#include <string>
+
+#include <dns_sd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <errno.h>
+
+#include "vine-constants.h"
+#include "vine-disc-plugin.h"
+#include "vine-log.h"
+#include "vine-utils.h"
+#include "dns-sd-plugin.h"
+
+#define VINE_DNS_SD_SERVICE_TYPE "_vine._tcp"
+#define VINE_DNS_SD_SERVICE_TYPE_LEN 10
+#define VINE_DNS_SD_SERVICE_TYPE_MAX_LEN VINE_MAX_SERVICE_TYPE_LEN + VINE_DNS_SD_SERVICE_TYPE_LEN + 1
+
+typedef struct {
+ int fd;
+ void *user_data; // vine_disc handle
+ char service_type[VINE_MAX_SERVICE_TYPE_LEN + 1];
+ map<int, DNSServiceRef> sdref_map; // <fd, DNSServiceRef>
+
+ DNSServiceRef get_addr_ref; // Used for only resolve_ip
+} vine_dns_sd_s;
+
+typedef struct {
+ char service_type[VINE_MAX_SERVICE_TYPE_LEN + 1];
+ char service_name[VINE_MAX_SERVICE_NAME_LEN + 1];
+ map<string, string> attributes;
+ int port;
+ vine_dns_sd_s *dns_sd_handle;
+} dns_sd_discovered_service_s;
+
+static vine_disc_plugin_callbacks event_callbacks;
+
+static vine_disc_error __convert_dns_sd_error_to_vine_disc_error(DNSServiceErrorType error)
+{
+ switch (error) {
+ case kDNSServiceErr_NoError:
+ return VINE_DISC_ERROR_NONE;
+ case kDNSServiceErr_BadParam:
+ return VINE_DISC_ERROR_INVALID_PARAMETER;
+ case kDNSServiceErr_NameConflict:
+ return VINE_DISC_ERROR_NAME_CONFLICT;
+ case kDNSServiceErr_ServiceNotRunning:
+ return VINE_DISC_ERROR_SERVICE_NOT_RUNNING;
+ default:
+ VINE_LOGE("Unknown error %d", error);
+ return VINE_DISC_ERROR_OPERATION_FAILED;
+ }
+}
+
+static dns_sd_discovered_service_s *__create_discovered_service(
+ const char *service_type, const char *service_name, vine_dns_sd_s *dns_sd_handle)
+{
+ dns_sd_discovered_service_s *service = new dns_sd_discovered_service_s;
+ strncpy(service->service_type, service_type, VINE_MAX_SERVICE_TYPE_LEN);
+ strncpy(service->service_name, service_name, VINE_MAX_SERVICE_NAME_LEN);
+ service->dns_sd_handle = dns_sd_handle;
+ return service;
+}
+
+static void __discovered_service_set_txt_record(dns_sd_discovered_service_s *service,
+ unsigned short txt_len, const unsigned char *txt_record)
+{
+ VINE_LOGD("Parse TXT record. txt_len[%d]", txt_len);
+ if (txt_len > MAX_TXT_RECORD_LEN) {
+ VINE_LOGE("Too long txt record. Max length[%d]", MAX_TXT_RECORD_LEN);
+ return;
+ }
+ if (txt_len <= 1) {
+ VINE_LOGD("No TXT record");
+ return;
+ }
+
+ const unsigned char *ptr = txt_record;
+ const unsigned char *max = txt_record + txt_len;
+ char key_buf[MAX_TXT_RECORD_LEN + 1];
+ char value_buf[MAX_TXT_RECORD_LEN + 1];
+
+ while (ptr < max) {
+ const unsigned char *const end = ptr + 1 + ptr[0];
+ if (end > max) {
+ VINE_LOGE("Invalid TXT data");
+ return;
+ }
+
+ char *buf = &key_buf[0];
+ while (ptr < end) {
+ ++ptr;
+ if (*ptr == '=') {
+ *buf = 0;
+ buf = &value_buf[0];
+ } else {
+ *buf = *ptr;
+ ++buf;
+ }
+ }
+ *buf = 0;
+ VINE_LOGD("Key[%s] Value[%s]", key_buf, value_buf);
+ string key(key_buf);
+ string value(value_buf);
+ service->attributes[key] = value;
+ }
+}
+
+static void __destroy_discovered_service(dns_sd_discovered_service_s *service)
+{
+ free(service);
+}
+
+/*
+static void __count_buffer_size(gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ int *buf_len = (int *)user_data;
+ int key_len = strlen((const char *)key);
+ int value_len = 0;
+ if (value == NULL)
+ *buf_len += 1 + key_len;
+ else if ((value_len = strlen((const char *)value)) == 0)
+ *buf_len += 1 + key_len + 1;
+ else
+ *buf_len += 1 + key_len + 1 + value_len;
+}
+*/
+
+void add_new_fd(vine_dns_sd_s *dns_sd_handle,
+ DNSServiceRef service_ref)
+{
+ int fd = DNSServiceRefSockFD(service_ref);
+ VINE_LOGD("Insert new service ref[%p] fd[%d]", service_ref, fd);
+
+ if (dns_sd_handle->sdref_map.find(fd) == dns_sd_handle->sdref_map.end()) {
+ dns_sd_handle->sdref_map[fd] = service_ref;
+ VINE_LOGD("New fd[%d] to be added", fd);
+ } else {
+ VINE_LOGI("Duplicate fd[%d]", fd);
+ }
+
+ if (event_callbacks.fd_added_cb)
+ event_callbacks.fd_added_cb(fd, dns_sd_handle->user_data);
+}
+
+static void __remove_service_ref(vine_dns_sd_s *dns_sd_handle, DNSServiceRef service_ref)
+{
+ int fd = DNSServiceRefSockFD(service_ref);
+ VINE_LOGD("fd[%d] to be removed", fd);
+ if (event_callbacks.fd_removed_cb)
+ event_callbacks.fd_removed_cb(fd, dns_sd_handle->user_data);
+
+ dns_sd_handle->sdref_map.erase(fd);
+ VINE_LOGD("Free service ref[%p]", service_ref);
+ DNSServiceRefDeallocate(service_ref);
+ // when the entry is removed, __free_service_ref will be called
+}
+
+static void __remove_service_ref_all(vine_dns_sd_s *dns_sd_handle)
+{
+ VINE_LOGD("Remove all DNSServiceRef for dns_sd_handle[%p]", dns_sd_handle);
+ for (auto iter = dns_sd_handle->sdref_map.begin();
+ iter != dns_sd_handle->sdref_map.end(); ++iter) {
+ int fd = iter->first;
+ DNSServiceRef service_ref = iter->second;
+
+ if (event_callbacks.fd_removed_cb)
+ event_callbacks.fd_removed_cb(fd, dns_sd_handle->user_data);
+ DNSServiceRefDeallocate(service_ref);
+
+ VINE_LOGD("fd[%d]/service_ref[%p] is removed.", fd, service_ref);
+ }
+ dns_sd_handle->sdref_map.clear();
+}
+
+static vine_disc_error __convert_attributes_to_txt_record(map<string, string> attributes,
+ uint16_t *txt_len, void **txt_record)
+{
+ vine_disc_error ret = VINE_DISC_ERROR_NONE;
+ TXTRecordRef txt_ref;
+ //char buf[MAX_TXT_RECORD_LEN] = {0, };
+ //int buf_len = MAX_TXT_RECORD_LEN;
+
+ TXTRecordCreate(&txt_ref, 0, NULL);
+
+ if (attributes.empty()) {
+ VINE_LOGE("No attributes\n");
+ return VINE_DISC_ERROR_NONE;
+ }
+// g_hash_table_foreach(attributes, __count_buffer_size, &buf_len);
+ //VINE_LOGD("Buffer Len: %d", buf_len);
+
+ for (const auto &kv : attributes) {
+ int r = TXTRecordSetValue(&txt_ref, kv.first.c_str(),
+ kv.second.size(), kv.second.c_str());
+ if (r != kDNSServiceErr_NoError) {
+ VINE_LOGE("TXTRecordSetValue failed %d", r);
+ // TODO: dns_sd_handle error?
+ return VINE_DISC_ERROR_OPERATION_FAILED;
+ }
+ }
+
+ *txt_len = TXTRecordGetLength(&txt_ref);
+ *txt_record = calloc(*txt_len, sizeof(char));
+ if (*txt_record == NULL) {
+ ret = VINE_DISC_ERROR_OUT_OF_MEMORY;
+ VINE_LOGE("Out of memory");
+ }
+
+ *txt_record = memcpy(*txt_record, TXTRecordGetBytesPtr(&txt_ref), *txt_len);
+ TXTRecordDeallocate(&txt_ref);
+ return ret;
+}
+
+static vine_disc_error get_if_index(const char *iface_name, unsigned int *if_index)
+{
+ *if_index = kDNSServiceInterfaceIndexAny;
+ if (iface_name != NULL) {
+ *if_index = if_nametoindex(iface_name);
+ if (*if_index == 0) {
+ VINE_LOGE("if_nametoindex error %d", errno);
+ return VINE_DISC_ERROR_INVALID_PARAMETER;
+ }
+ }
+ return VINE_DISC_ERROR_NONE;
+}
+
+static void __register_reply(DNSServiceRef sdRef, DNSServiceFlags flags,
+ DNSServiceErrorType errorCode, const char *name,
+ const char *regtype, const char *domain, void *context)
+{
+ VINE_LOGD("Service Registerd. service name[%s] error[%d]", name, errorCode);
+ vine_dns_sd_s *dns_sd_handle = (vine_dns_sd_s *)context;
+ vine_disc_error error = __convert_dns_sd_error_to_vine_disc_error(errorCode);
+
+ if (event_callbacks.published_cb)
+ event_callbacks.published_cb(dns_sd_handle,
+ name, error, dns_sd_handle->user_data);
+}
+
+static void __get_ip_address_by_str(const struct sockaddr *address,
+ sa_family_t *family, char *ip)
+{
+ const unsigned char *addr;
+ if (address->sa_family == AF_INET) {
+ const struct sockaddr_in *s4 = ((struct sockaddr_in *)address);
+ addr = (const unsigned char *)&s4->sin_addr;
+ } else if (address->sa_family == AF_INET6) {
+ const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *)address;
+ addr = (const unsigned char *)&s6->sin6_addr;
+ } else {
+ VINE_LOGE("Invalid address family");
+ return;
+ }
+ *family = address->sa_family;
+ inet_ntop(*family, addr, ip, VINE_MAX_IP_LEN + 1);
+ ip[VINE_MAX_IP_LEN] = '\0';
+}
+
+static void __get_addr_info_reply(DNSServiceRef sdRef,
+ DNSServiceFlags flags, uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode, const char *hostname,
+ const struct sockaddr *address, uint32_t ttl, void *context)
+{
+ VINE_LOGD("Address Resolved. service[%p] flags[%u] hostname[%s] ttl[%u]",
+ context, flags, hostname, ttl);
+ RET_IF(address == NULL, "Address is null");
+
+ vine_dns_sd_s *dns_sd_handle = (vine_dns_sd_s *)context;
+ sa_family_t address_family = AF_INET;
+ char ip[VINE_MAX_IP_LEN + 1];
+
+ if (!event_callbacks.ip_resolved_cb) {
+ VINE_LOGD("No callbacks");
+ return;
+ }
+
+ /*
+ if (flags & kDNSServiceFlagsMoreComing) {
+ VINE_LOGI("+ More results are queued");
+ return;
+ }
+ */
+
+ __get_ip_address_by_str(address, &address_family, ip);
+
+ bool add = flags & kDNSServiceFlagsAdd;
+ event_callbacks.ip_resolved_cb(dns_sd_handle, add,
+ ip, address_family, dns_sd_handle->user_data);
+}
+
+vine_disc_error dns_sd_resolve_ip(void *plugin_handle,
+ const char *service_type, const char *service_name,
+ const char *host_name, const char *iface_name)
+{
+ RET_VAL_IF(!plugin_handle, VINE_DISC_ERROR_INVALID_PARAMETER, "plugin_handle is NULL");
+ RET_VAL_IF(!service_type, VINE_DISC_ERROR_INVALID_PARAMETER, "service_type is NULL");
+ RET_VAL_IF(!service_name, VINE_DISC_ERROR_INVALID_PARAMETER, "service_name is NULL");
+ RET_VAL_IF(!host_name, VINE_DISC_ERROR_INVALID_PARAMETER, "host_name is NULL");
+
+ VINE_LOGD("Start to resolve IP. plugin_handle[%p]\n", plugin_handle);
+ VINE_LOGD("type[%s] name[%s] host[%s] iface[%s]",
+ service_type, service_name, host_name, iface_name);
+
+ vine_dns_sd_s *dns_sd_handle = (vine_dns_sd_s *)plugin_handle;
+ DNSServiceRef service_ref;
+ DNSServiceFlags flags = 0;
+ unsigned int if_index = kDNSServiceInterfaceIndexAny;
+ vine_disc_error ret = get_if_index(iface_name, &if_index);
+ RET_VAL_IF(ret != VINE_DISC_ERROR_NONE, ret, "get_if_index");
+
+ DNSServiceProtocol protocol = kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6;
+ DNSServiceErrorType err = DNSServiceGetAddrInfo(&service_ref,
+ flags, if_index, protocol, host_name,
+ __get_addr_info_reply, dns_sd_handle);
+ if (err != kDNSServiceErr_NoError) {
+ // TODO: Error handling
+ VINE_LOGE("DNSServiceGetAddrInfo fails");
+ return VINE_DISC_ERROR_OPERATION_FAILED;
+ }
+
+ dns_sd_handle->get_addr_ref = service_ref;
+ add_new_fd(dns_sd_handle, service_ref);
+
+ return VINE_DISC_ERROR_NONE;
+}
+
+vine_disc_error dns_sd_cancel_resolve_ip(void *plugin_handle)
+{
+ RET_VAL_IF(!plugin_handle, VINE_DISC_ERROR_INVALID_PARAMETER, "plugin_handle is NULL");
+ VINE_LOGD("Cancel resolving IP. plugin_handle[%p]\n", plugin_handle);
+
+ vine_dns_sd_s *dns_sd_handle = (vine_dns_sd_s *)plugin_handle;
+ DNSServiceRef service_ref = dns_sd_handle->get_addr_ref;
+
+ VINE_LOGD("Free service ref[%p]", service_ref);
+ __remove_service_ref(dns_sd_handle, service_ref);
+ dns_sd_handle->get_addr_ref = NULL;
+
+ return VINE_DISC_ERROR_NONE;
+}
+
+static void __resolve_reply(DNSServiceRef sdRef, unsigned int flags,
+ unsigned int if_index, DNSServiceErrorType error_code,
+ const char *fullname, const char *host_name,
+ unsigned short port, unsigned short txt_len,
+ const unsigned char *txt_record, void *context)
+{
+ VINE_LOGD("Service Resolved[%p]. flags[%u]", context, flags);
+ VINE_LOGD("fullname[%s] host_name[%s]", fullname, host_name);
+ VINE_LOGD("port[%d], txt_len[%d]", port, txt_len);
+
+ RET_IF(context == NULL, "service is NULL");
+ dns_sd_discovered_service_s *service = (dns_sd_discovered_service_s *)context;
+ vine_dns_sd_s *dns_sd_handle = service->dns_sd_handle;
+
+ service->port = (int)ntohs(port);
+ __discovered_service_set_txt_record(service, txt_len, txt_record);
+
+ /*
+ if (flags & kDNSServiceFlagsMoreComing) {
+ VINE_LOGI("+ More results are queued");
+ return;
+ }
+ */
+ char iface_name[IF_NAMESIZE + 1] = {0, };
+ if (if_indextoname(if_index, iface_name))
+ event_callbacks.discovered_cb(dns_sd_handle, true,
+ service->service_type, service->service_name, host_name,
+ service->port, service->attributes, iface_name,
+ flags & kDNSServiceFlagsMoreComing, dns_sd_handle->user_data);
+ else
+ VINE_LOGE("if_indextoname fails %d", errno);
+
+ __remove_service_ref(dns_sd_handle, sdRef);
+ __destroy_discovered_service(service);
+}
+
+static void __resolve_service_name(vine_dns_sd_s *dns_sd_handle,
+ unsigned int flags, unsigned int if_index,
+ const char *service_type,
+ const char *domain, dns_sd_discovered_service_s *service)
+{
+ VINE_LOGD("Resolve a service[%p]. dns_sd_handle[%p]",
+ service, dns_sd_handle);
+ RET_IF(service == NULL, "service is NULL");
+ VINE_LOGD("service_type[%s] service_name[%s]",
+ service_type, service->service_name);
+
+ DNSServiceRef service_ref;
+ DNSServiceErrorType err =
+ DNSServiceResolve(&service_ref, flags, if_index,
+ service->service_name, service_type, domain,
+ __resolve_reply, service);
+ if (err != kDNSServiceErr_NoError) {
+ VINE_LOGE("Fail to resolve service");
+ __destroy_discovered_service(service);
+ return;
+ }
+
+ add_new_fd(dns_sd_handle, service_ref);
+}
+
+static void __browse_reply(DNSServiceRef sdRef, unsigned int flags,
+ unsigned int if_index, DNSServiceErrorType error_code,
+ const char *service_name, const char *service_type,
+ const char *domain, void *context)
+{
+ VINE_LOGD("Service Discovered. dns_sd_handle[%p]. flags[%u]", context, flags);
+ VINE_LOGD("service_type[%s] service_name[%s]", service_type, service_name);
+ VINE_LOGD("domain[%s]", domain);
+
+ vine_dns_sd_s *dns_sd_handle = (vine_dns_sd_s *)context;
+
+ if (error_code != kDNSServiceErr_NoError) {
+ VINE_LOGE("Fail to browse dns-sd service. error[%d]", error_code);
+ __remove_service_ref(dns_sd_handle, sdRef);
+ return;
+ }
+ // vine_disc_error error = __convert_dns_sd_error_to_vine_disc_error(error_code);
+
+ char iface_name[IF_NAMESIZE + 1] = {0, };
+ if (if_indextoname(if_index, iface_name) == NULL) {
+ VINE_LOGE("if_indextoname fails. %d", errno);
+ __remove_service_ref(dns_sd_handle, sdRef);
+ return;
+ }
+
+ if (flags & kDNSServiceFlagsAdd) {
+ dns_sd_discovered_service_s *service =
+ __create_discovered_service(dns_sd_handle->service_type,
+ service_name, dns_sd_handle);
+ VINE_LOGD("New service is found [%p]", service);
+ __resolve_service_name(dns_sd_handle, flags, if_index,
+ service_type, domain, service);
+ } else {
+ VINE_LOGD("Service is unavailable");
+ map<string, string> empty_map;
+ if (event_callbacks.discovered_cb)
+ event_callbacks.discovered_cb(dns_sd_handle, false,
+ dns_sd_handle->service_type, service_name, NULL,
+ 0, empty_map, iface_name,
+ flags & kDNSServiceFlagsMoreComing,
+ dns_sd_handle->user_data);
+ }
+}
+
+vine_disc_error dns_sd_init(void **plugin_handle, void *disc_handle)
+{
+ vine_dns_sd_s *handle = new vine_dns_sd_s;
+
+ handle->fd = -1;
+ handle->user_data = disc_handle;
+ *plugin_handle = handle;
+ return VINE_DISC_ERROR_NONE;
+}
+
+void dns_sd_deinit(void *plugin_handle)
+{
+ RET_IF(plugin_handle == NULL, "Plugin handle is null");
+ vine_dns_sd_s *dns_sd_handle = (vine_dns_sd_s *)plugin_handle;
+
+ auto it = dns_sd_handle->sdref_map.begin();
+ while (it != dns_sd_handle->sdref_map.end()) {
+ DNSServiceRefDeallocate(it->second);
+ it = dns_sd_handle->sdref_map.erase(it);
+ }
+
+ free(dns_sd_handle);
+}
+
+vine_disc_error dns_sd_publish(void *plugin_handle, const char *service_type,
+ const char *service_name, int port, const map<string, string> &attributes,
+ const char *iface_name)
+{
+ RET_VAL_IF(!plugin_handle, VINE_DISC_ERROR_INVALID_PARAMETER, "plugin_handle is NULL");
+ RET_VAL_IF(!service_type, VINE_DISC_ERROR_INVALID_PARAMETER, "service_type is NULL");
+ RET_VAL_IF(!service_name, VINE_DISC_ERROR_INVALID_PARAMETER, "service_name is NULL");
+
+ vine_disc_error ret = VINE_DISC_ERROR_NONE;
+ vine_dns_sd_s *dns_sd_handle = (vine_dns_sd_s *)plugin_handle;
+ DNSServiceRef service_ref;
+ DNSServiceErrorType error = kDNSServiceErr_NoError;
+ DNSServiceFlags flags = 0;
+ unsigned int if_index = kDNSServiceInterfaceIndexAny;
+ ret = get_if_index(iface_name, &if_index);
+ RET_VAL_IF(ret != VINE_DISC_ERROR_NONE, ret, "get_if_index");
+
+ VINE_LOGD("Publish a service. plugin_handle[%p]\n", plugin_handle);
+ const char *domain = NULL;
+ const char *host = NULL;
+ uint16_t txt_len = 0;
+ void *txt_record = NULL;
+
+ ret = __convert_attributes_to_txt_record(attributes, &txt_len, &txt_record);
+ RET_VAL_IF(ret != VINE_DISC_ERROR_NONE, ret, "Fail to convert attributes to txt record");
+ // txt_record should be freed after DNSServiceRegister if ret is not error
+
+ char stype[VINE_DNS_SD_SERVICE_TYPE_MAX_LEN + 1];
+ snprintf(stype, VINE_DNS_SD_SERVICE_TYPE_MAX_LEN,
+ VINE_DNS_SD_SERVICE_TYPE ",%s", service_type);
+ VINE_LOGD("DNS-SD service type %s <-- %s", stype, service_type);
+
+ error = DNSServiceRegister(&service_ref, flags, if_index,
+ service_name, stype,
+ domain, host,
+ htons(port),
+ txt_len, txt_record,
+ __register_reply, dns_sd_handle);
+
+ free(txt_record);
+
+ if (error != kDNSServiceErr_NoError)
+ VINE_LOGE("DNSServiceRegister failed %d", error);
+ else
+ add_new_fd(dns_sd_handle, service_ref);
+
+ return __convert_dns_sd_error_to_vine_disc_error(error);
+}
+
+vine_disc_error dns_sd_stop_publish(void *plugin_handle)
+{
+ RET_VAL_IF(!plugin_handle, VINE_DISC_ERROR_INVALID_PARAMETER, "plugin_handle is NULL");
+ __remove_service_ref_all((vine_dns_sd_s *)plugin_handle);
+ return VINE_DISC_ERROR_NONE;
+}
+
+vine_disc_error dns_sd_subscribe(void *plugin_handle,
+ const char *service_type, const char *iface_name)
+{
+ RET_VAL_IF(!plugin_handle, VINE_DISC_ERROR_INVALID_PARAMETER, "plugin_handle is NULL");
+ RET_VAL_IF(!service_type, VINE_DISC_ERROR_INVALID_PARAMETER, "service_type is NULL");
+
+ vine_dns_sd_s *dns_sd_handle = (vine_dns_sd_s *)plugin_handle;
+ DNSServiceRef service_ref;
+ DNSServiceErrorType error = kDNSServiceErr_NoError;
+ DNSServiceFlags flags = 0;
+ unsigned int if_index = kDNSServiceInterfaceIndexAny;
+ vine_disc_error ret = get_if_index(iface_name, &if_index);
+ RET_VAL_IF(ret != VINE_DISC_ERROR_NONE, ret, "get_if_index");
+
+ VINE_LOGD("Subscribe a service. plugin_handle[%p]", plugin_handle);
+ const char *domain = NULL;
+
+ strncpy(dns_sd_handle->service_type, service_type, VINE_MAX_SERVICE_TYPE_LEN);
+ char stype[VINE_DNS_SD_SERVICE_TYPE_MAX_LEN + 1];
+ snprintf(stype, VINE_DNS_SD_SERVICE_TYPE_MAX_LEN,
+ VINE_DNS_SD_SERVICE_TYPE ",%s", service_type);
+ VINE_LOGD("DNS-SD service type %s <-- %s", stype, service_type);
+
+ error = DNSServiceBrowse(&service_ref, flags, if_index,
+ stype, domain, __browse_reply, dns_sd_handle);
+
+ if (error != kDNSServiceErr_NoError)
+ VINE_LOGE("DNSServiceBrowse failed %d", error);
+ else
+ add_new_fd(dns_sd_handle, service_ref);
+ return __convert_dns_sd_error_to_vine_disc_error(error);
+}
+
+vine_disc_error dns_sd_stop_subscribe(void *plugin_handle)
+{
+ RET_VAL_IF(!plugin_handle, VINE_DISC_ERROR_INVALID_PARAMETER, "plugin_handle is NULL");
+ __remove_service_ref_all((vine_dns_sd_s *)plugin_handle);
+ return VINE_DISC_ERROR_NONE;
+}
+
+vine_disc_error dns_sd_process_event(void *plugin_handle, int fd)
+{
+ VINE_LOGD("Process DNS-SD event plugin_handle[%p] fd[%d]", plugin_handle, fd);
+ RET_VAL_IF(!plugin_handle, VINE_DISC_ERROR_INVALID_PARAMETER, "plugin_handle is NULL");
+ vine_dns_sd_s *dns_sd_handle = (vine_dns_sd_s *)plugin_handle;
+
+ DNSServiceRef service_ref = dns_sd_handle->sdref_map[fd];
+ RET_VAL_IF(service_ref == NULL, VINE_DISC_ERROR_INVALID_PARAMETER,
+ "No DNSServiceRef for fd");
+ VINE_LOGD("service ref[%p] to be processed", service_ref);
+ DNSServiceErrorType err = DNSServiceProcessResult(service_ref);
+ if (err) {
+ VINE_LOGE("DNSServiceProcessResult return %d", err);
+ return __convert_dns_sd_error_to_vine_disc_error(err);
+ }
+
+ return VINE_DISC_ERROR_NONE;
+}
+
+void dns_sd_register_callbacks(vine_disc_plugin_callbacks callbacks)
+{
+ event_callbacks.published_cb = callbacks.published_cb;
+ event_callbacks.discovered_cb = callbacks.discovered_cb;
+ event_callbacks.ip_resolved_cb = callbacks.ip_resolved_cb;
+ event_callbacks.fd_added_cb = callbacks.fd_added_cb;
+ event_callbacks.fd_removed_cb = callbacks.fd_removed_cb;
+}
+
+void vine_disc_plugin_init(vine_disc_plugin_fn *fn)
+{
+ fn->init = dns_sd_init;
+ fn->deinit = dns_sd_deinit;
+ fn->publish = dns_sd_publish;
+ fn->stop_publish = dns_sd_stop_publish;
+ fn->subscribe = dns_sd_subscribe;
+ fn->stop_subscribe = dns_sd_stop_subscribe;
+ fn->resolve_ip = dns_sd_resolve_ip;
+ fn->cancel_resolve_ip = dns_sd_cancel_resolve_ip;
+ fn->register_callbacks = dns_sd_register_callbacks;
+ fn->process_event = dns_sd_process_event;
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+#ifndef __DNS_SD_PLUGIN_H__
+#define __DNS_SD_PLUGIN_H__
+
+#define MAX_TXT_RECORD_LEN 1300 // Refer Section 6.2 in http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt
+
+#endif
--- /dev/null
+# Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+SET(LIBWEBSOCKETS_PLUGIN "vine-plugin-libwebsockets")
+
+SET(LIBWEBSOCKETS_PLUGIN_VERSION_MAJOR "1")
+SET(LIBWEBSOCKETS_PLUGIN_VERSION_MINOR "0")
+SET(LIBWEBSOCKETS_PLUGIN_VERSION_PATCH "0")
+SET(LIBWEBSOCKETS_PLUGIN_VERSION ${LIBWEBSOCKETS_PLUGIN_VERSION_MAJOR}.${LIBWEBSOCKETS_PLUGIN_VERSION_MINOR}.${LIBWEBSOCKETS_PLUGIN_VERSION_PATCH})
+
+SET(VINE_PATH ${PROJECT_SOURCE_DIR}/src)
+
+IF(ENABLE_DATAPATH_PLUGIN_DEBUG)
+ ADD_DEFINITIONS("-DENABLE_DATAPATH_PLUGIN_DEBUG")
+ENDIF(ENABLE_DATAPATH_PLUGIN_DEBUG)
+
+IF(USE_LIBWEBSOCKETS_STATIC)
+ SET(WEBSOCKETS_INCLUDE_PATH "${PROJECT_SOURCE_DIR}/third-party/libwebsockets/include")
+ENDIF(USE_LIBWEBSOCKETS_STATIC)
+
+IF(USE_LIBWEBSOCKETS_STATIC_PREBUILT)
+ SET(WEBSOCKETS_INCLUDE_PATH ${PREBUILT_THIRD_PARTY_INCLUDE_PATH})
+ SET(PREBUILT_PATH "${PROJECT_SOURCE_DIR}/third-party/prebuilt/${BUILD_OS}/${BUILD_ARCH}")
+ SET(LIBWEBSOCKETS_STATIC_LIB "${PREBUILT_PATH}/libwebsockets.a")
+ENDIF(USE_LIBWEBSOCKETS_STATIC_PREBUILT)
+
+INCLUDE_DIRECTORIES(
+ ${${fw_name}_INCLUDE_DIRS}
+ ${VINE_PATH}/include
+ ${WEBSOCKETS_INCLUDE_PATH}
+ ${VINE_LOGGER_PATH}
+ ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
+FILE(GLOB VINE_PLUGIN_SOURCES *.cpp)
+
+ADD_DEFINITIONS("-fvisibility=default")
+ADD_LIBRARY(${LIBWEBSOCKETS_PLUGIN} SHARED ${VINE_PLUGIN_SOURCES})
+
+SET_TARGET_PROPERTIES(
+ ${LIBWEBSOCKETS_PLUGIN}
+ PROPERTIES
+ SOVERSION ${LIBWEBSOCKETS_PLUGIN_VERSION_MAJOR}
+)
+
+IF(ANDROID)
+ MESSAGE("NDK_INCLUDE_DIR " ${NDK_INCLUDE_DIR})
+ INCLUDE_DIRECTORIES("${NDK_INCLUDE_DIR}")
+IF(NOT USE_LIBWEBSOCKETS_STATIC)
+ SET(LIBWEBSOCKETS "${NDK_LIB_DIR}/libwebsockets.so")
+ENDIF(NOT USE_LIBWEBSOCKETS_STATIC)
+ SET(LIBCRYPTO "${NDK_LIB_DIR}/libcrypto.so")
+ SET(LIBSSL "${NDK_LIB_DIR}/libssl.so")
+ELSE(ANDROID)
+IF(NOT USE_LIBWEBSOCKETS_STATIC)
+ find_library(
+ LIBWEBSOCKETS
+ NAMES libwebsockets.so
+ HINTS /usr/local/lib /usr/lib /usr/lib64
+ REQUIRED
+ )
+ENDIF(NOT USE_LIBWEBSOCKETS_STATIC)
+ find_library(
+ LIBCRYPTO
+ NAMES libcrypto.so
+ HINTS /usr/local/lib /usr/lib /usr/lib64 /usr/lib/x86_64-linux-gnu
+ REQUIRED
+ )
+ find_library(
+ LIBSSL
+ NAMES libssl.so
+ HINTS /usr/local/lib /usr/lib /usr/lib64 /usr/lib/x86_64-linux-gnu
+ REQUIRED
+ )
+ENDIF(ANDROID)
+
+IF(USE_LIBWEBSOCKETS_STATIC)
+IF(USE_LIBWEBSOCKETS_STATIC_PREBUILT)
+ MESSAGE("Use prebuilt libwebsockets static library.")
+ SET(LIBWEBSOCKETS libwebsockets)
+ ADD_LIBRARY(libwebsockets STATIC IMPORTED)
+ SET_PROPERTY(TARGET libwebsockets PROPERTY IMPORTED_LOCATION ${LIBWEBSOCKETS_STATIC_LIB})
+ELSE(USE_LIBWEBSOCKETS_STATIC_PREBUILT)
+ MESSAGE("Use libwebsockets static library.")
+ SET(LIBWEBSOCKETS ${LIBWEBSOCKETS_LIBRARIES_STATIC})
+ENDIF(USE_LIBWEBSOCKETS_STATIC_PREBUILT)
+ENDIF(USE_LIBWEBSOCKETS_STATIC)
+
+SET(VINE_DEPS_LIB ${LIBWEBSOCKETS} ${LIBCRYPTO})
+TARGET_LINK_LIBRARIES(${LIBWEBSOCKETS_PLUGIN}
+ ${LIBWEBSOCKETS}
+ ${VINE_LOGGER}
+ ${LIBCRYPTO}
+ ${LIBSSL}
+ dl
+)
+
+INSTALL(TARGETS ${LIBWEBSOCKETS_PLUGIN} DESTINATION "${LIB_DIR}")
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+
+#include <map>
+
+#include <libwebsockets.h>
+#include <openssl/ssl.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <sys/eventfd.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#include "vine-data-path-plugin.h"
+
+#include "vine-log.h"
+#include "vine-utils.h"
+#include "vine-queue.h"
+
+#ifdef ENABLE_DATAPATH_PLUGIN_DEBUG
+#define DEBUG_LEVEL (LLL_USER | LLL_ERR | LLL_WARN | LLL_DEBUG | LLL_NOTICE | LLL_INFO)
+#else
+#define DEBUG_LEVEL 0
+#endif
+
+using namespace std;
+
+typedef struct {
+ unsigned char *buf;
+ size_t len;
+} websocket_data_s;
+
+typedef struct {
+ struct lws *wsi;
+ struct lws_vhost *vh;
+ bool close_requested;
+ int curr_conn;
+ int max_conn;
+ VineQueue<websocket_data_s *> *recv_buffer;
+ VineQueue<websocket_data_s *> *write_buffer;
+ void *user; // vine_data_path_h
+} websocket_s;
+
+typedef enum {
+ WEBSOCKET_OP_OPEN = 0,
+ WEBSOCKET_OP_CONNECT,
+ WEBSOCKET_OP_WRITE,
+ WEBSOCKET_OP_TERMINATE,
+} websocket_op_code_e;
+
+typedef struct {
+ websocket_op_code_e code;
+ websocket_s *ws;
+ int addr_family;
+ char *ip; // connect only
+ int port; // open, connect only
+ char *iface_name; // open, connect only
+ int max_conn; // open only
+ vine_dp_ssl ssl; // open, connect only
+} websocket_op_s;
+
+static VineQueue<websocket_op_s *> op_queue;
+
+static int g_ref_count = 0;
+static pthread_mutex_t g_lws_mutex = PTHREAD_MUTEX_INITIALIZER;
+static vine_dp_plugin_callbacks g_callbacks = {
+ .pollfd_cb = NULL,
+ .opened_cb = NULL,
+ .accepted_cb = NULL,
+ .connected_cb = NULL,
+ .received_cb = NULL,
+ .written_cb = NULL,
+ .terminated_cb = NULL,
+};
+
+static map<int, struct lws_pollfd *> g_pollfds;
+static struct lws_context *g_context = NULL;
+
+static int _websocket_protocol_cb(struct lws *wsi,
+ enum lws_callback_reasons reason, void *user, void *in, size_t len);
+static struct lws_protocols protocols[] = {
+ {"vine-websocket-protocol", _websocket_protocol_cb, sizeof(websocket_s), 0, 0, NULL, 0},
+ { NULL, NULL, 0, 0 } /* terminator */
+};
+
+static void websocket_process_event(int fd, int events);
+static void _connect_server(websocket_s *ws,
+ int addr_family, const char *ip, int port, const char *iface_name, vine_dp_ssl ssl);
+static void _open_server(websocket_s *ws, int addr_family,
+ int port, const char *iface_name, int max_conn, vine_dp_ssl ssl);
+static void _request_write(websocket_s *ws);
+
+static int _save_data(websocket_s *ws, void *buf, size_t len);
+static int _write_data(websocket_s *ws);
+
+static void _debug_func(int level, const char *line)
+{
+ if (line)
+ VINE_LOGD("[libwebsockets:%d] %s", level, line);
+}
+
+static void _get_peer_network_info(struct lws *wsi, char ip[], int *port)
+{
+ struct sockaddr_storage addr;
+ socklen_t len = sizeof(addr);
+ int fd;
+
+ fd = lws_get_socket_fd(wsi);
+ getpeername(fd, (struct sockaddr*)&addr, &len);
+
+ if (addr.ss_family == AF_INET) {
+ struct sockaddr_in *s = (struct sockaddr_in*)&addr;
+ *port = ntohs(s->sin_port);
+ inet_ntop(AF_INET, &s->sin_addr, ip, INET6_ADDRSTRLEN);
+ } else {
+ struct sockaddr_in6 *s = (struct sockaddr_in6*)&addr;
+ *port = ntohs(s->sin6_port);
+ inet_ntop(AF_INET6, &s->sin6_addr, ip, INET6_ADDRSTRLEN);
+ }
+
+ VINE_LOGD("address family[%d] peer ip[%s], peer port[%d]\n", addr.ss_family, ip, *port);
+}
+
+static void _notify_websocket_op_request()
+{
+ uint64_t v = 1;
+ int _eventfd = eventfd(0, 0);
+
+ g_callbacks.pollfd_cb(VINE_DATA_PATH_POLLFD_OP, _eventfd, LWS_POLLOUT);
+ if (write(_eventfd, &v, sizeof(v)) == -1)
+ VINE_LOGE("Write error(%d)", errno);
+}
+
+static int _add_websocket_op_request(websocket_op_code_e code,
+ websocket_s *ws, int addr_family,
+ const char *ip, int port, const char *iface_name,
+ int max_conn,
+ vine_dp_ssl *ssl)
+{
+ websocket_op_s *op = (websocket_op_s *)calloc(1, sizeof(websocket_op_s));
+ if (!op)
+ return -1;
+
+ op->code = code;
+ op->ws = ws;
+ op->addr_family = addr_family;
+ op->ip = STRDUP(ip);
+ op->port = port;
+ op->iface_name = STRDUP(iface_name);
+ op->max_conn = max_conn;
+
+ if (ssl) {
+ op->ssl.use_ssl = ssl->use_ssl;
+ op->ssl.tls_version = ssl->tls_version;
+ op->ssl.ca_path = STRDUP(ssl->ca_path);
+ op->ssl.cert_path = STRDUP(ssl->cert_path);
+ op->ssl.key_path = STRDUP(ssl->key_path);
+ op->ssl.vflags = ssl->vflags;
+ }
+
+ op_queue.push(op);
+
+ return 0;
+}
+
+static void _del_websocket_op_request(websocket_op_s *op)
+{
+ free(op->ip);
+ free(op->iface_name);
+ free(op->ssl.ca_path);
+ free(op->ssl.cert_path);
+ free(op->ssl.key_path);
+ free(op);
+}
+
+static void _process_websocket_op_request(void)
+{
+ websocket_op_s *op = op_queue.pop();
+ RET_IF(op == NULL, "op is NULL");
+
+ if (!op->ws) {
+ _del_websocket_op_request(op);
+ return;
+ }
+
+ switch (op->code) {
+ case WEBSOCKET_OP_OPEN:
+ _open_server(op->ws, op->addr_family, op->port, op->iface_name, op->max_conn, op->ssl);
+ break;
+ case WEBSOCKET_OP_CONNECT:
+ _connect_server(op->ws, op->addr_family, op->ip, op->port, op->iface_name, op->ssl);
+ break;
+ case WEBSOCKET_OP_WRITE:
+ _request_write(op->ws);
+ break;
+ default:
+ break;
+ }
+
+ _del_websocket_op_request(op);
+}
+
+static struct lws_context *_create_context(void)
+{
+ struct lws_context_creation_info info;
+
+ memset(&info, 0, sizeof info);
+ info.iface = NULL;
+ info.protocols = protocols;
+ info.extensions = 0;
+ info.gid = -1;
+ info.uid = -1;
+ info.pt_serv_buf_size = VINE_DATA_PATH_MAX_BUFFER_SIZE;
+#if (LWS_LIBRARY_VERSION_MAJOR >= 4)
+ info.register_notifier_list = NULL;
+#endif
+ info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT |
+ LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;
+
+ return lws_create_context(&info);
+}
+
+static void _destroy_context(struct lws_context *context)
+{
+ lws_context_destroy(context);
+}
+
+static void _add_websocket_poll_fd(struct lws_pollargs *args)
+{
+ RET_IF(args == NULL, "args is NULL");
+
+ struct lws_pollfd *pollfd = (struct lws_pollfd *)calloc(1, sizeof(struct lws_pollfd));
+ RET_IF(pollfd == NULL, "Failed to allocate pollfd");
+
+ pollfd->fd = args->fd;
+ pollfd->events = args->events;
+
+ g_pollfds[pollfd->fd] = pollfd;
+ VINE_LOGI("websocket pollfd[%p] fd[%d] is added.", pollfd, args->fd);
+
+ if (g_callbacks.pollfd_cb)
+ g_callbacks.pollfd_cb(VINE_DATA_PATH_POLLFD_ADD, pollfd->fd, pollfd->events);
+}
+
+static void _delete_websocket_poll_fd(struct lws_pollargs *args)
+{
+ RET_IF(args == NULL, "args is NULL");
+
+ auto it = g_pollfds.find(args->fd);
+ if (it == g_pollfds.end()) {
+ VINE_LOGE("No fd[%d]", args->fd);
+ }
+
+ free(it->second);
+ g_pollfds.erase(it);
+ VINE_LOGI("websocket poll fd[%d] is removed.", args->fd);
+
+ if (g_callbacks.pollfd_cb)
+ g_callbacks.pollfd_cb(VINE_DATA_PATH_POLLFD_DEL, args->fd, args->events);
+}
+
+static void _change_websocket_poll_fd(struct lws_pollargs *args)
+{
+ RET_IF(args == NULL, "args is NULL");
+
+ auto it = g_pollfds.find(args->fd);
+ RET_IF(it == g_pollfds.end(), "pollfd is NULL");
+
+ struct lws_pollfd *pollfd = it->second;
+ pollfd->events = args->events;
+
+ if (g_callbacks.pollfd_cb)
+ g_callbacks.pollfd_cb(VINE_DATA_PATH_POLLFD_MOD, pollfd->fd, pollfd->events);
+}
+
+// For debug
+static void __print_received_data(void *in, size_t len)
+{
+ char *data = (char *)calloc(len, sizeof(char));
+ memcpy(data, in, len);
+ VINE_LOGD(">> len[%zd] received[%s]", len, data);
+ free(data);
+}
+
+static int _websocket_protocol_cb(struct lws *wsi,
+ enum lws_callback_reasons reason, void *user, void *in, size_t len)
+{
+ websocket_s *ws = (websocket_s *)user; // Per session data. allocated automatically by lws.
+ websocket_s *vhd = (websocket_s *)lws_protocol_vh_priv_get(lws_get_vhost(wsi),
+ lws_get_protocol(wsi)); // host private data.
+ int n = 0;
+
+ switch (reason) {
+ case LWS_CALLBACK_FILTER_NETWORK_CONNECTION :
+ if (vhd && vhd->max_conn > 0 && vhd->max_conn <= vhd->curr_conn) {
+ VINE_LOGI("The max connection limit[%d] is reached. Reject the request.",
+ vhd->max_conn);
+ // Return non-zero to terminate the connection.
+ return -1;
+ }
+ break;
+
+ /* --- external poll() management support --- */
+ case LWS_CALLBACK_ADD_POLL_FD:
+ _add_websocket_poll_fd((struct lws_pollargs *)in);
+ break;
+
+ case LWS_CALLBACK_DEL_POLL_FD:
+ _delete_websocket_poll_fd((struct lws_pollargs *)in);
+ break;
+
+ case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
+ _change_websocket_poll_fd((struct lws_pollargs *)in);
+ break;
+
+ case LWS_CALLBACK_LOCK_POLL:
+ pthread_mutex_lock(&g_lws_mutex);
+ break;
+
+ case LWS_CALLBACK_UNLOCK_POLL:
+ pthread_mutex_unlock(&g_lws_mutex);
+ break;
+
+ /* --- protocol lifecycle callbacks --- */
+ case LWS_CALLBACK_PROTOCOL_INIT:
+ break;
+
+ case LWS_CALLBACK_PROTOCOL_DESTROY:
+ break;
+
+ /* --- serving callbacks --- */
+ case LWS_CALLBACK_ESTABLISHED: {
+ VINE_LOGI("Websocket connection is established.");
+ // When client is connected, ws(wsi->user_space) is automatically allocated by lws.
+ // So lws has the ownership of a user data.
+ // Reset a user data to own the memory.
+ websocket_s *client_ws = (websocket_s *)calloc(1, sizeof(websocket_s));
+ if (!client_ws)
+ return 0;
+ client_ws->wsi = wsi;
+ lws_set_wsi_user(wsi, client_ws);
+ vhd->curr_conn++;
+
+ if (g_callbacks.accepted_cb) {
+ char ip[INET6_ADDRSTRLEN];
+ int port;
+ _get_peer_network_info(wsi, ip, &port);
+ g_callbacks.accepted_cb(ip, port, client_ws, vhd->user);
+ }
+ break;
+ }
+
+ case LWS_CALLBACK_RECEIVE:
+ VINE_LOGI("%d bytes is received from client.", len);
+ __print_received_data(in, len);
+ if (ws && g_callbacks.received_cb) {
+ _save_data(ws, in, len);
+ g_callbacks.received_cb(len, ws->user);
+ }
+ break;
+
+ case LWS_CALLBACK_SERVER_WRITEABLE:
+ if (!ws) {
+ VINE_LOGI("plugin handle is NULL. ignore a writable event.");
+ return 0;
+ }
+
+ if (ws->close_requested) {
+ VINE_LOGI("Close websocket.");
+ return -1;
+ }
+
+ VINE_LOGI("Send data to client.");
+ n = _write_data(ws);
+ if (n < 0) {
+ VINE_LOGE("fatal error occured. need to close a connection.");
+ return -1;
+ }
+
+ if (g_callbacks.written_cb)
+ g_callbacks.written_cb(n, ws->user);
+ break;
+
+ case LWS_CALLBACK_CLOSED:
+ VINE_LOGI("Connected client is closed.");
+ vhd->curr_conn--;
+
+ // call terminated_cb() when close isn't requested only.
+ if (ws && !ws->close_requested && g_callbacks.terminated_cb)
+ g_callbacks.terminated_cb(ws->user);
+ break;
+
+ /* --- client callbacks --- */
+ case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
+ VINE_LOGI("Failed connection request to server.");
+ if (ws && g_callbacks.connected_cb)
+ g_callbacks.connected_cb(-1, ws->user);
+ break;
+
+ case LWS_CALLBACK_CLIENT_ESTABLISHED:
+ VINE_LOGI("Connected with server.");
+ if (ws && g_callbacks.connected_cb)
+ g_callbacks.connected_cb(0, ws->user);
+ break;
+
+ case LWS_CALLBACK_CLIENT_RECEIVE:
+ VINE_LOGI("%d bytes is received from server.", len);
+ __print_received_data(in, len);
+ if (ws && g_callbacks.received_cb) {
+ _save_data(ws, in, len);
+ g_callbacks.received_cb(len, ws->user);
+ }
+ break;
+
+ case LWS_CALLBACK_CLIENT_WRITEABLE:
+ if (!ws) {
+ VINE_LOGI("plugin handle is NULL. ignore a writable event.");
+ return 0;
+ }
+
+ if (ws->close_requested) {
+ VINE_LOGI("Close websocket.");
+ return -1;
+ }
+
+ VINE_LOGI("Writeable to server.");
+ n = _write_data(ws);
+ if (g_callbacks.written_cb)
+ g_callbacks.written_cb(n, ws->user);
+ break;
+
+ case LWS_CALLBACK_CLIENT_CLOSED:
+ VINE_LOGI("Connected server is closed.");
+ // call terminated_cb() when close isn't requested only.
+ if (ws && !ws->close_requested && g_callbacks.terminated_cb)
+ g_callbacks.terminated_cb(ws->user);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int websocket_init(void)
+{
+ if (__sync_fetch_and_add(&g_ref_count, 1) > 0) {
+ VINE_LOGI("lws is already initialized.");
+ return VINE_DATA_PATH_ERROR_NONE;
+ }
+
+ lws_set_log_level(DEBUG_LEVEL, _debug_func);
+ g_context = _create_context();
+ RET_VAL_IF(!g_context, VINE_DATA_PATH_ERROR_OPERATION_FAILED, "failed to create context");
+
+ VINE_LOGD("lws is initialized.");
+ return VINE_DATA_PATH_ERROR_NONE;
+}
+
+static void websocket_deinit(void)
+{
+ if (__sync_sub_and_fetch(&g_ref_count, 1) > 0) {
+ VINE_LOGE("Do nothing. lws is still used.");
+ return;
+ }
+
+ _destroy_context(g_context);
+ g_context = NULL;
+
+ auto it = g_pollfds.begin();
+ while (it != g_pollfds.end()) {
+ free(it->second);
+ it = g_pollfds.erase(it);
+ }
+
+ VINE_LOGD("lws is deinitialized.");
+}
+
+static long __get_ssl_ctx_options(vine_dp_tls_version_e tls_version)
+{
+ long options = SSL_OP_ALL;
+
+ VINE_LOGD("Get SSL ctx options for TLS %d", tls_version);
+
+ switch (tls_version) {
+ case VINE_DP_TLS_VERSION_1_3: // TLSv1.3 or later
+ options |= SSL_OP_NO_TLSv1_2;
+ case VINE_DP_TLS_VERSION_1_2: // TLSv1.2 or later
+ options |= SSL_OP_NO_TLSv1_1;
+ case VINE_DP_TLS_VERSION_1_1: // TLSv1.1 or later
+ options |= SSL_OP_NO_TLSv1;
+ case VINE_DP_TLS_VERSION_1_0: // TLSv1.0 or later
+ case VINE_DP_TLS_VERSION_DEFAULT: // Default: TLSv1.0 or later
+ options |= SSL_OP_NO_SSLv2;
+ options |= SSL_OP_NO_SSLv3;
+ break;
+ default:
+ VINE_LOGE("Invalid TLS version.");
+ break;
+ }
+
+ return options;
+}
+
+static struct lws_vhost *_create_vhost(int addr_family,
+ int port, const char *iface_name, vine_dp_ssl ssl)
+{
+ struct lws_context_creation_info info;
+ long ssl_options = 0;
+
+ memset(&info, 0, sizeof(info));
+
+ info.port = port;
+
+ ssl_options = __get_ssl_ctx_options(ssl.tls_version);
+ if (port == CONTEXT_PORT_NO_LISTEN) // client
+ info.ssl_client_options_set = ssl_options;
+ else // server
+ info.ssl_options_set = ssl_options;
+
+ info.iface = iface_name;
+ info.ssl_ca_filepath = ssl.ca_path;
+ info.ssl_cert_filepath = ssl.cert_path;
+ info.ssl_private_key_filepath = ssl.key_path;
+ info.protocols = protocols;
+ info.vhost_name = "vine-websocket-server";
+ info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
+
+ if (addr_family == VINE_DP_IPV4)
+ info.options |= LWS_SERVER_OPTION_DISABLE_IPV6;
+ else if (addr_family == VINE_DP_IPV6)
+ info.options |= LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE;
+ // do nothing in case of VINE_DP_IPV4_IPV6
+
+ return lws_create_vhost(g_context, &info);
+}
+
+static void _open_server(websocket_s *ws, int addr_family,
+ int port, const char *iface_name, int max_conn, vine_dp_ssl ssl)
+{
+ ws->max_conn = max_conn;
+ ws->vh = _create_vhost(addr_family, port, iface_name, ssl);
+ if (!ws->vh) {
+ VINE_LOGE("Failed to create vhost.");
+ if (g_callbacks.opened_cb)
+ g_callbacks.opened_cb(VINE_DATA_PATH_ERROR_OPERATION_FAILED, -1, ws->user);
+ return;
+ }
+
+ void *user_data = lws_protocol_vh_priv_zalloc(ws->vh, protocols, sizeof(websocket_s));
+ memcpy(user_data, ws, sizeof(websocket_s));
+
+ int vport = lws_get_vhost_port(ws->vh);
+ if (g_callbacks.opened_cb)
+ g_callbacks.opened_cb(VINE_DATA_PATH_ERROR_NONE, vport, ws->user);
+}
+
+static int websocket_open(vine_dp_plugin_h handle,
+ int addr_family, int port, const char *iface_name, int max_conn, vine_dp_ssl ssl)
+{
+ RET_VAL_IF(!handle, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "handle is NULL");
+ RET_VAL_IF(!g_context, VINE_DATA_PATH_ERROR_INVALID_OPERATION, "g_context is NULL");
+
+ if (_add_websocket_op_request(WEBSOCKET_OP_OPEN,
+ (websocket_s *)handle, addr_family, NULL, port, iface_name, max_conn, &ssl) < 0)
+ return VINE_DATA_PATH_ERROR_OPERATION_FAILED;
+
+ _notify_websocket_op_request();
+
+ return VINE_DATA_PATH_ERROR_NONE;
+}
+
+static void _connect_server(websocket_s *ws, int addr_family, const char *ip, int port,
+ const char *iface_name, vine_dp_ssl ssl)
+{
+ struct lws_client_connect_info client_conn_info;
+
+ memset(&client_conn_info, 0, sizeof(client_conn_info));
+ client_conn_info.context = g_context;
+ client_conn_info.port = port;
+ client_conn_info.address = ip;
+ client_conn_info.iface = iface_name;
+ client_conn_info.path = "/";
+ client_conn_info.host = client_conn_info.address;
+ client_conn_info.origin = client_conn_info.address;
+ client_conn_info.protocol = "vine-websocket-protocol";
+
+ if (ssl.use_ssl)
+ client_conn_info.ssl_connection |= LCCSCF_USE_SSL;
+
+ if (ssl.vflags & VINE_DATA_PATH_V_FLAG_ALLOW_SS_CERTIFICATE)
+ client_conn_info.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED;
+
+ if (ssl.vflags & VINE_DATA_PATH_V_FLAG_SKIP_CN_CHECK)
+ client_conn_info.ssl_connection |= LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK;
+
+ client_conn_info.userdata = (void *)ws;
+ client_conn_info.vhost = _create_vhost(addr_family, CONTEXT_PORT_NO_LISTEN, iface_name, ssl);
+ ws->wsi = lws_client_connect_via_info(&client_conn_info);
+}
+
+static int websocket_connect(vine_dp_plugin_h handle,
+ int addr_family, const char *ip, int port, const char *iface_name, vine_dp_ssl ssl)
+{
+ RET_VAL_IF(!handle, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "handle is NULL");
+ RET_VAL_IF(!g_context, VINE_DATA_PATH_ERROR_INVALID_OPERATION, "g_context is NULL");
+
+ if (_add_websocket_op_request(WEBSOCKET_OP_CONNECT,
+ (websocket_s *)handle, addr_family, ip, port, iface_name, 0, &ssl) < 0)
+ return VINE_DATA_PATH_ERROR_OPERATION_FAILED;
+
+ _notify_websocket_op_request();
+
+ return VINE_DATA_PATH_ERROR_NONE;
+}
+
+static void __destroy_websocket_data(websocket_data_s *wd)
+{
+ RET_IF(wd == NULL, "data is NULL.");
+ free(wd->buf);
+ free(wd);
+}
+
+static int _save_data(websocket_s *ws, void *buf, size_t len)
+{
+ websocket_data_s *wd = (websocket_data_s *)calloc(1, sizeof(websocket_data_s));
+ RET_VAL_IF(!wd, -1, "Out of memory.");
+ unsigned char *data = (unsigned char *)calloc(len, sizeof(unsigned char));
+ if (!data) {
+ VINE_LOGE("Out of memory.");
+ free(wd);
+ return -1;
+ }
+
+ memcpy(data, buf, len);
+ wd->buf = data;
+ wd->len = len;
+ ws->recv_buffer->push(wd);
+ VINE_LOGD("websocket_data[%p] is pushed to recv_buffer.", wd);
+
+ return len;
+}
+
+static int _write_data(websocket_s *ws)
+{
+ websocket_data_s *wd = NULL;
+ int bytes = 0;
+
+ if (!ws->write_buffer)
+ return bytes;
+
+ while ((wd = ws->write_buffer->pop())) {
+ unsigned char *out = (unsigned char *)calloc(LWS_PRE + wd->len,
+ sizeof(unsigned char));
+ memcpy(out + LWS_PRE, wd->buf, wd->len);
+ int n = lws_write(ws->wsi, out + LWS_PRE, wd->len, LWS_WRITE_BINARY);
+ if (n < 0) {
+ free(out);
+ __destroy_websocket_data(wd);
+ return n;
+ }
+
+ bytes += n;
+
+ free(out);
+ __destroy_websocket_data(wd);
+ wd = NULL;
+ }
+
+ return bytes;
+}
+
+static int websocket_read(vine_dp_plugin_h handle, unsigned char *buf, size_t len)
+{
+ RET_VAL_IF(handle == NULL, 0, "plugin handle is NULL");
+ RET_VAL_IF(buf == NULL, 0, "buf is NULL");
+ RET_VAL_IF(len == 0, 0, "len is 0");
+
+ websocket_s *ws = (websocket_s *)handle;
+ websocket_data_s *wd = NULL;
+ size_t bytes = 0;
+
+ if (!ws->recv_buffer)
+ return bytes;
+
+ wd = ws->recv_buffer->front();
+ RET_VAL_IF(wd == NULL, 0, "There is no data.");
+
+ if (wd->len <= len) {
+ memcpy(buf, wd->buf, wd->len);
+ bytes = wd->len;
+ wd = ws->recv_buffer->pop();
+ __destroy_websocket_data(wd);
+ wd = NULL;
+ } else {
+ memcpy(buf, wd->buf, len);
+ bytes = len;
+
+ // Requeue a remained data ahead of the items in recv_buffer.
+ // It might be read later.
+ size_t remained_len = wd->len - len;
+ unsigned char *remained_data = (unsigned char *)calloc(remained_len, sizeof(unsigned char));
+ memcpy(remained_data, wd->buf + len, remained_len);
+ free(wd->buf);
+ wd->buf = remained_data;
+ wd->len = remained_len;
+ }
+
+ return bytes;
+}
+
+static void _request_write(websocket_s *ws)
+{
+ VINE_LOGI("Request write callback for %p", ws);
+ lws_callback_on_writable(ws->wsi);
+}
+
+static int websocket_write(vine_dp_plugin_h handle, unsigned char *buf, size_t len)
+{
+ RET_VAL_IF(handle == NULL, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "plugin handle is NULL");
+ RET_VAL_IF(buf == NULL, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "buf is NULL");
+ RET_VAL_IF(len == 0, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "len is 0");
+
+ websocket_s *ws = (websocket_s *)handle;
+ websocket_data_s *wd = (websocket_data_s *)calloc(1, sizeof(websocket_data_s));
+ unsigned char *data = (unsigned char *)calloc(len, sizeof(unsigned char));
+
+ memcpy(data, buf, len);
+ wd->buf = data;
+ wd->len = len;
+
+ ws->write_buffer->push(wd);
+ VINE_LOGD("websocket_data[%p] is pushed to write_buffer.", wd);
+
+ if (_add_websocket_op_request(WEBSOCKET_OP_WRITE, ws, 0, NULL, -1, NULL, 0, NULL) < 0)
+ return VINE_DATA_PATH_ERROR_OPERATION_FAILED;
+
+ _notify_websocket_op_request();
+
+ return VINE_DATA_PATH_ERROR_NONE;
+}
+
+static int websocket_close(vine_dp_plugin_h handle)
+{
+ RET_VAL_IF(handle == NULL, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "plugin handle is NULL");
+ websocket_s *ws = (websocket_s *)handle;
+
+ ws->close_requested = true;
+ if (ws->wsi)
+ lws_callback_on_writable(ws->wsi);
+
+ return VINE_DATA_PATH_ERROR_NONE;
+}
+
+static void websocket_process_event(int fd, int events)
+{
+ auto it = g_pollfds.find(fd);
+ if (it == g_pollfds.end()) {
+ _process_websocket_op_request();
+ close(fd);
+ return;
+ }
+
+ struct lws_pollfd *pollfd = it->second;
+
+ // Ignore POLLHUP event.
+ if (!(events & pollfd->events & LWS_POLLIN) && (events & LWS_POLLHUP))
+ return;
+
+ // Update revents manually.
+ pollfd->revents = events;
+
+ // Service polled scoket.
+ lws_service_fd(g_context, pollfd);
+
+ // Check for any connection needing forced service.
+ while (!lws_service_adjust_timeout(g_context, 1, 0)) {
+ // Service any pending webscoket activity.
+ // Only needed if multiple service threads.
+ lws_service_tsi(g_context, -1, 0);
+ }
+}
+
+static int websocket_create(vine_dp_plugin_h *handle, void *plugin_data, void *user)
+{
+ RET_VAL_IF(handle == NULL, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "handle is NULL");
+
+ websocket_s *ws = plugin_data ?
+ (websocket_s *)plugin_data : (websocket_s *)calloc(1, sizeof(websocket_s));
+ RET_VAL_IF(ws == NULL, VINE_DATA_PATH_ERROR_OUT_OF_MEMORY, "Out of memory");
+
+ VINE_LOGD("plugin_data[%p]", plugin_data);
+
+ ws->close_requested = false;
+ ws->curr_conn = 0;
+ ws->max_conn = 0;
+ ws->user = user;
+ ws->recv_buffer = new VineQueue<websocket_data_s *>;
+ ws->write_buffer = new VineQueue<websocket_data_s *>;
+ *handle = ws;
+
+ return VINE_DATA_PATH_ERROR_NONE;
+}
+
+static int websocket_destroy(vine_dp_plugin_h handle)
+{
+ RET_VAL_IF(handle == NULL, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "handle is NULL");
+
+ websocket_s *ws = (websocket_s *)handle;
+
+ ws->wsi = NULL;
+ ws->vh = NULL;
+ ws->user = NULL;
+
+ delete ws->recv_buffer;
+ delete ws->write_buffer;
+
+ ws->recv_buffer = NULL;
+ ws->write_buffer = NULL;
+
+ free(ws);
+
+ return VINE_DATA_PATH_ERROR_NONE;
+}
+
+static void websocket_register_callbacks(vine_dp_plugin_callbacks callbacks)
+{
+ g_callbacks = callbacks;
+}
+
+void vine_data_path_plugin_init(vine_dp_plugin_fn *fn)
+{
+ fn->init = websocket_init;
+ fn->deinit = websocket_deinit;
+ fn->register_callbacks = websocket_register_callbacks;
+ fn->process_event = websocket_process_event;
+
+ fn->create = websocket_create;
+ fn->destroy = websocket_destroy;
+ fn->open = websocket_open;
+ fn->connect = websocket_connect;
+ fn->read = websocket_read;
+ fn->write = websocket_write;
+ fn->close = websocket_close;
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+#ifndef __LIBWEBSOCKETS_PLUGIN_H__
+#define __LIBWEBSOCKETS_PLUGIN_H__
+
+
+#endif
--- /dev/null
+{
+ global: main;
+ _IO_*;
+ local: *;
+};
--- /dev/null
+bin/remote-camera__DEL__W/Qf7Iv/rlOHOI/WU4R+dp8KH/kwOsgtJkqWiOP5VlcO0zMFijVMflmzRHudCADRM4YfZ8jxu+YE
+z79WIkun9A==
+shared/res/remote-camera.png__DEL__BTGBG483ma/3HgWnreWF2xbdzr4F0gTRWjwxoSV+7kKBWt4UJTuv5WE4DhKdU4in2kwn/55O/qPD
+bNc/wgCuGg==
+tizen-manifest.xml__DEL__MAvhx2V7YqYKApmNN2efDMDv+UChyq9EpwuLbhHBVbHZMhkvB2tz30WDvoPrPrN2S1vGUFXrlflW
+MW21NIqtUg==
+author-signature.xml__DEL__cbFUOE5oICV/rKArfFN8sOPb1o7BBAURv77cpT/U21yUi62FJZeKNkrMSNoJneArra5DTURlp1EQ
++xUbaBXNzA==
--- /dev/null
+<Signature xmlns="http://www.w3.org/2000/09/xmldsig#" Id="AuthorSignature">
+<SignedInfo>
+<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod>
+<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"></SignatureMethod>
+<Reference URI="bin%2Fremote-camera">
+<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
+<DigestValue>W/Qf7Iv/rlOHOI/WU4R+dp8KH/kwOsgtJkqWiOP5VlcO0zMFijVMflmzRHudCADRM4YfZ8jxu+YE
+z79WIkun9A==</DigestValue>
+</Reference>
+<Reference URI="shared%2Fres%2Fremote-camera.png">
+<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
+<DigestValue>BTGBG483ma/3HgWnreWF2xbdzr4F0gTRWjwxoSV+7kKBWt4UJTuv5WE4DhKdU4in2kwn/55O/qPD
+bNc/wgCuGg==</DigestValue>
+</Reference>
+<Reference URI="tizen-manifest.xml">
+<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
+<DigestValue>MAvhx2V7YqYKApmNN2efDMDv+UChyq9EpwuLbhHBVbHZMhkvB2tz30WDvoPrPrN2S1vGUFXrlflW
+MW21NIqtUg==</DigestValue>
+</Reference>
+<Reference URI="#prop">
+<Transforms>
+<Transform Algorithm="http://www.w3.org/2006/12/xml-c14n11"></Transform>
+</Transforms>
+<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
+<DigestValue>aXbSAVgmAz0GsBUeZ1UmNDRrxkWhDUVGb45dZcNRq429wX3X+x6kaXT3NdNDTSNVTU+ypkysPMGv
+QY10fG1EWQ==</DigestValue>
+</Reference>
+</SignedInfo>
+<SignatureValue>
+SdiZbxQuiL2gI/Tmly+8BiS5WkhcYEZMbUFsQ1kUUJ9dD6e1iz+LxkgHM3DMS1vH6sM1EBzM17A8
+Sb5rJzTsBlP8mxDIbbqKwVDiXwAe5Z02B8Uaz2n41VhAMDwfR5PjWE9uRL/KV622NiZ6QeG6ujme
+T+cHAbBWQxh01S8GIVk=
+</SignatureValue>
+<KeyInfo>
+<X509Data>
+<X509Certificate>
+MIICmzCCAYOgAwIBAgIGAT8T+Ms5MA0GCSqGSIb3DQEBBQUAMFYxGjAYBgNVBAoMEVRpemVuIEFz
+c29jaWF0aW9uMRowGAYDVQQLDBFUaXplbiBBc3NvY2lhdGlvbjEcMBoGA1UEAwwTVGl6ZW4gRGV2
+ZWxvcGVycyBDQTAeFw0xMjExMDEwMDAwMDBaFw0xOTAxMDEwMDAwMDBaMBcxFTATBgNVBAMMDHRp
+emVuX2F1dGhvcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAssi+tZOAmBSLiy8JTOk7gXrk
+Nlg8Jq+O+4yd14a407BTbmp9r4qfNlUKLpYRL0TyU84XUWrLHHXe9aO2G223v/DUz3f182W5JWK0
+HvHt0vYcBycOBkXCc5y26funq7qhEgC9e1NNxIc1mFYD0jJiRNJgxIRCw33Ca/8qdGPY1K8CAwEA
+AaMyMDAwDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwDQYJ
+KoZIhvcNAQEFBQADggEBAJweqlVjN2GHF01jlE1HfZNFJ5Y7Ye+NagJnNdj6iVOPGP9J9PVOmllB
+8S8pon1fUXPvrHFe8gNFg1klLMJaPbeDQM+UiPWn80A+8e4gJpHz+E9on2ZYkEBCZHOXSxxzaS7D
+0bwj3eotbpY/duepn3U91fcLcnoehO3oe4pawrC6AObRjqY/Sh9I4j8btHFxRRVpsa7VkSg/5JB4
+2jfKlJ2otI8bYYuOiYeZC2ody9PsHzlqmqlo+J68RlxAemPMovRqGeK0XDqJEt7dJEzknQmp0732
+fhAZNyXVlukjdR8+3+f1J5vu4MEqT/7aLXLOwLN/qgUfCBA2YQ/YqMJvf9Y=
+</X509Certificate>
+<X509Certificate>
+MIIDOTCCAiGgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMRowGAYDVQQKDBFUaXplbiBBc3NvY2lh
+dGlvbjEaMBgGA1UECwwRVGl6ZW4gQXNzb2NpYXRpb24xHjAcBgNVBAMMFVRpemVuIERldmVsb3Bl
+cnMgUm9vdDAeFw0xMjAxMDEwMDAwMDBaFw0yNzAxMDEwMDAwMDBaMFYxGjAYBgNVBAoMEVRpemVu
+IEFzc29jaWF0aW9uMRowGAYDVQQLDBFUaXplbiBBc3NvY2lhdGlvbjEcMBoGA1UEAwwTVGl6ZW4g
+RGV2ZWxvcGVycyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANVGhRGmMIUyBA7o
+PCz8Sxut6z6HNkF4oDIuzuKaMzRYPeWodwe9O0gmqAkToQHfwg2giRhE5GoPld0fq+OYMMwSasCu
+g8dwODx1eDeSYVuOLWRxpAmbTXOsSFi6VoWeyaPEm18JBHvZBsU5YQtgZ6Kp7MqzvQg3pXOxtajj
+vyHxiatJl+xXrHgcXC1wgyG3buty7u/Fi2mvKXJ0PRJcCjjK81dqe/Vr20sRUCrbk02zbm5ggFt/
+jIEhV8wbFRQpliobc7J4dSTKhFfrqGM8rdd54LYhD7gSI1CFSe16pUXfcVR7FhJztRaiGLnCrwBE
+dyTZ248+D4L/qR/D0axb3jcCAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOC
+AQEAnOXXQ/1O/QTDHyrmQDtFziqPY3xWlJBqJtEqXiT7Y+Ljpe66e+Ee/OjQMlZe8gu21/8cKklH
+95RxjopMWCVedXDUbWdvS2+CdyvVW/quT2E0tjqIzXDekUTYwwhlPWlGxvfj3VsxqSFq3p8Brl04
+1Gx5RKAGyKVsMfTLhbbwSWwApuBUxYfcNpKwLWGPXkysu+HctY03OKv4/xKBnVWiN8ex/Sgesi0M
++OBAOMdZMPK32uJBTeKFx1xZgTLIhk45V0hPOomPjZloiv0LSS11eyd451ufjW0iHRE7WlpR6EvI
+W6TFyZgMpQq+kg4hWl2SBTf3s2VI8Ygz7gj8TMlClg==
+</X509Certificate>
+</X509Data>
+</KeyInfo>
+<Object Id="prop"><SignatureProperties xmlns:dsp="http://www.w3.org/2009/xmldsig-properties"><SignatureProperty Id="profile" Target="#AuthorSignature"><dsp:Profile URI="http://www.w3.org/ns/widgets-digsig#profile"></dsp:Profile></SignatureProperty><SignatureProperty Id="role" Target="#AuthorSignature"><dsp:Role URI="http://www.w3.org/ns/widgets-digsig#role-author"></dsp:Role></SignatureProperty><SignatureProperty Id="identifier" Target="#AuthorSignature"><dsp:Identifier></dsp:Identifier></SignatureProperty></SignatureProperties></Object>
+</Signature>
\ No newline at end of file
--- /dev/null
+<Signature xmlns="http://www.w3.org/2000/09/xmldsig#" Id="DistributorSignature">
+<SignedInfo>
+<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod>
+<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"></SignatureMethod>
+<Reference URI="author-signature.xml">
+<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
+<DigestValue>cbFUOE5oICV/rKArfFN8sOPb1o7BBAURv77cpT/U21yUi62FJZeKNkrMSNoJneArra5DTURlp1EQ
++xUbaBXNzA==</DigestValue>
+</Reference>
+<Reference URI="bin%2Fremote-camera">
+<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
+<DigestValue>W/Qf7Iv/rlOHOI/WU4R+dp8KH/kwOsgtJkqWiOP5VlcO0zMFijVMflmzRHudCADRM4YfZ8jxu+YE
+z79WIkun9A==</DigestValue>
+</Reference>
+<Reference URI="shared%2Fres%2Fremote-camera.png">
+<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
+<DigestValue>BTGBG483ma/3HgWnreWF2xbdzr4F0gTRWjwxoSV+7kKBWt4UJTuv5WE4DhKdU4in2kwn/55O/qPD
+bNc/wgCuGg==</DigestValue>
+</Reference>
+<Reference URI="tizen-manifest.xml">
+<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
+<DigestValue>MAvhx2V7YqYKApmNN2efDMDv+UChyq9EpwuLbhHBVbHZMhkvB2tz30WDvoPrPrN2S1vGUFXrlflW
+MW21NIqtUg==</DigestValue>
+</Reference>
+<Reference URI="#prop">
+<Transforms>
+<Transform Algorithm="http://www.w3.org/2006/12/xml-c14n11"></Transform>
+</Transforms>
+<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
+<DigestValue>/r5npk2VVA46QFJnejgONBEh4BWtjrtu9x/IFeLksjWyGmB/cMWKSJWQl7aU3YRQRZ3AesG8gF7q
+GyvKX9Snig==</DigestValue>
+</Reference>
+</SignedInfo>
+<SignatureValue>
+owo7gNXCqtVi1woInvLxh0wVxz3QN/PweWKlWoSkDyw7XqdAd0oTxBfWBubm8r1EyzS/4WN9hSVa
+o3bE07xrlSu34YZ+Xj7lV1L0W9msGcGeeJJiZsx7Txm8Rt8rrBZDWtJ+vgm3NW6a2+38cfJlLomq
+ldovHjCsd1KLz7xPOkI=
+</SignatureValue>
+<KeyInfo>
+<X509Data>
+<X509Certificate>
+MIICtzCCAiACCQCX7BAlwjllkTANBgkqhkiG9w0BAQUFADCBnTELMAkGA1UEBhMCS1IxDjAMBgNV
+BAgMBVN1d29uMQ4wDAYDVQQHDAVTdXdvbjEWMBQGA1UECgwNVGl6ZW4gVGVzdCBDQTEiMCAGA1UE
+CwwZVGl6ZW4gRGlzdHJpYnV0b3IgVGVzdCBDQTEyMDAGA1UEAwwpVGl6ZW4gUGFydG5lci1NYW51
+ZmFjdHVyZXIgRGlzdHJpYnV0b3IgQ0EwHhcNMTIxMjEzMDU0NDI3WhcNMjIxMjExMDU0NDI3WjCB
+oTELMAkGA1UEBhMCS1IxDjAMBgNVBAgMBVN1d29uMQ4wDAYDVQQHDAVTdXdvbjEWMBQGA1UECgwN
+VGl6ZW4gVGVzdCBDQTEiMCAGA1UECwwZVGl6ZW4gRGlzdHJpYnV0b3IgVGVzdCBDQTE2MDQGA1UE
+AwwtVGl6ZW4gUGFydG5lci1NYW51ZmFjdHVyZXIgRGlzdHJpYnV0b3IgU2lnbmVyMIGfMA0GCSqG
+SIb3DQEBAQUAA4GNADCBiQKBgQC2O0i2ou9Pa8KI+RVu6/1QAMichy8YqDtpM4cogiX+tYfafN/v
+4l7q9sQdotaFPLWPR1HNtG6O6SKWZmM/Hp2U+S3vDeylEhex/IopWF9RZMPJFyKr9bJnlzTqiZuz
+qItlXOZZBHhUL73v6OxqYg2BsA88jLtCoZWVAkoKDX93ZQIDAQABMA0GCSqGSIb3DQEBBQUAA4GB
+AJ5vn+dEhnZFuRdIMUt8TwQzXSxPMqNmR8+fcwwzAacxeOTgeK1C+DqE31vead/c8CVDMurZHQbs
+qH/Ala6FomTm1q0uuM3Ll2qZaPkI3qr+h37YD4AI5scyIlP7ppApcdPRSkDgqfXkika62Q9M0+MN
+nxl8+AvEukgC6qcd90oL
+</X509Certificate>
+<X509Certificate>
+MIICzzCCAjigAwIBAgIJAMNp1nbwir4GMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYDVQQGEwJLUjEO
+MAwGA1UECAwFU3V3b24xDjAMBgNVBAcMBVN1d29uMRYwFAYDVQQKDA1UaXplbiBUZXN0IENBMSIw
+IAYDVQQLDBlUaXplbiBEaXN0cmlidXRvciBUZXN0IENBMTcwNQYDVQQDDC5UaXplbiBQYXJ0bmVy
+LU1hbnVmYWN0dXJlciBEaXN0cmlidXRvciBSb290IENBMB4XDTEyMTIxMzA1NDQyMFoXDTIyMTIx
+MTA1NDQyMFowgZ0xCzAJBgNVBAYTAktSMQ4wDAYDVQQIDAVTdXdvbjEOMAwGA1UEBwwFU3V3b24x
+FjAUBgNVBAoMDVRpemVuIFRlc3QgQ0ExIjAgBgNVBAsMGVRpemVuIERpc3RyaWJ1dG9yIFRlc3Qg
+Q0ExMjAwBgNVBAMMKVRpemVuIFBhcnRuZXItTWFudWZhY3R1cmVyIERpc3RyaWJ1dG9yIENBMIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMni4Qmv96ww/De92kYhtzpas7viwyYoFTTuvcmayD
+FBuDoaHqrSDsrb9M7qrR4N9higg1mT8a9J8CUhPLKhlifK9OwyQsZS+8xVfPfogIJ3wumfjLl5zi
+nqpkSe2ernxOTU+PB5lWU8toKbnJO3+jchm14DjCQ4ZoqG26qbG5pQIDAQABoxAwDjAMBgNVHRME
+BTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAA9lsDyArDR9balTfqrjN241nVqyLqPOP+MFRjGWrIlI
+yLDcw2/L9hxguD8XhgAanaQUzS06XM6BajDjcSiJrohjoMFkB09M+r5fVkrhkT29oABsKXO87+p8
+JbbbIfdwMUrAjXsrjMUzJ3J6fn86/dn/PcuMAkCgrD069IzpE8NM
+</X509Certificate>
+</X509Data>
+</KeyInfo>
+<Object Id="prop"><SignatureProperties xmlns:dsp="http://www.w3.org/2009/xmldsig-properties"><SignatureProperty Id="profile" Target="#DistributorSignature"><dsp:Profile URI="http://www.w3.org/ns/widgets-digsig#profile"></dsp:Profile></SignatureProperty><SignatureProperty Id="role" Target="#DistributorSignature"><dsp:Role URI="http://www.w3.org/ns/widgets-digsig#role-distributor"></dsp:Role></SignatureProperty><SignatureProperty Id="identifier" Target="#DistributorSignature"><dsp:Identifier></dsp:Identifier></SignatureProperty></SignatureProperties></Object>
+</Signature>
\ No newline at end of file
--- /dev/null
+#!/bin/bash
+ROOTSTRAP="mobile-6.0-device.core"
+
+function clean {
+ echo "Clean build directory"
+ rm -rf Debug
+}
+
+function pack {
+ echo "Packaging.."
+ tizen package -t tpk -- ./Debug
+}
+
+function build {
+ echo "Build.."
+ tizen build-native -r $ROOTSTRAP -a arm -C Debug -- .
+}
+
+if [ $# -eq 1 ]
+then
+case $1 in
+ "clean")
+ clean
+ ;;
+ "build")
+ build
+ ;;
+ "pack")
+ pack
+ ;;
+esac
+fi
+
--- /dev/null
+#ifndef __remote_camera_H__
+#define __remote_camera_H__
+
+#include <app.h>
+#include <Elementary.h>
+#include <system_settings.h>
+#include <efl_extension.h>
+#include <dlog.h>
+#include <camera.h>
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "remote-camera"
+
+#if !defined(PACKAGE)
+#define PACKAGE "org.example.remote-camera"
+#endif
+
+#define LOGD(fmt, args...) dlog_print(DLOG_DEBUG, LOG_TAG, fmt, ##args)
+#define LOGE(fmt, args...) dlog_print(DLOG_ERROR, LOG_TAG, fmt, ##args)
+
+#define RET_IF_ERROR(ret, func) do {\
+ if (ret != CAMERA_ERROR_NONE) {\
+ LOGE("%s %d", func, ret);\
+ return;\
+ }\
+ } while (0)
+
+
+void capture_image();
+#endif /* __remote_camera_H__ */
--- /dev/null
+#ifndef VINE_SERVER_H
+#define VINE_SERVER_H
+
+//#include <vine.h>
+
+void start_vine_server(void *data);
+
+#endif
--- /dev/null
+APPNAME = remote-camera
+
+type = app
+profile = mobile-6.0
+
+USER_SRCS = src/*.c
+USER_DEFS =
+USER_INC_DIRS = inc
+USER_OBJS =
+USER_LIBS =
+USER_EDCS =
--- /dev/null
+#include "remote-camera.h"
+#include "vine-server.h"
+
+#include <camera.h>
+#include <storage.h>
+
+typedef struct appdata {
+ Evas_Object *win;
+ Evas_Object *conform;
+
+ Evas_Object *rect;
+ Evas *evas;
+
+ camera_h camera;
+ char *path;
+} appdata_s;
+
+static void
+win_delete_request_cb(void *data, Evas_Object *obj, void *event_info)
+{
+ ui_app_exit();
+}
+
+static void
+win_back_cb(void *data, Evas_Object *obj, void *event_info)
+{
+ appdata_s *ad = data;
+ /* Let window go to hide state. */
+ elm_win_lower(ad->win);
+}
+
+static void
+create_base_gui(appdata_s *ad)
+{
+ /* Window */
+ /* Create and initialize elm_win.
+ elm_win is mandatory to manipulate window. */
+ ad->win = elm_win_util_standard_add(PACKAGE, PACKAGE);
+ elm_win_autodel_set(ad->win, EINA_TRUE);
+
+ evas_object_smart_callback_add(ad->win, "delete,request", win_delete_request_cb, NULL);
+ eext_object_event_callback_add(ad->win, EEXT_CALLBACK_BACK, win_back_cb, ad);
+
+ /* Conformant */
+ /* Create and initialize elm_conformant.
+ elm_conformant is mandatory for base gui to have proper size
+ when indicator or virtual keypad is visible. */
+ ad->conform = elm_conformant_add(ad->win);
+ elm_win_indicator_mode_set(ad->win, ELM_WIN_INDICATOR_SHOW);
+ elm_win_indicator_opacity_set(ad->win, ELM_WIN_INDICATOR_OPAQUE);
+ evas_object_size_hint_weight_set(ad->conform, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ elm_win_resize_object_add(ad->win, ad->conform);
+ evas_object_show(ad->conform);
+
+ ad->evas = evas_object_evas_get(ad->win);
+
+ /*
+ ad->rect = evas_object_image_add(ad->evas);
+ evas_object_image_size_set(ad->rect, 240, 320);
+ evas_object_image_fill_set(ad->rect, 0, 0, 240, 320);
+ evas_object_resize(ad->rect, 240, 320);
+ evas_object_show(ad->rect);
+ evas_object_show(ad->win);
+ */
+
+ int x, y, w, h;
+ elm_win_screen_size_get(ad->win, &x, &y, &w, &h);
+ ad->rect = evas_object_rectangle_add(ad->evas);
+ evas_object_resize(ad->rect, w, h);
+ evas_object_move(ad->rect, x, y);
+ evas_object_color_set(ad->rect, 0, 0, 0, 0);
+ evas_object_render_op_set(ad->rect, EVAS_RENDER_COPY);
+ evas_object_size_hint_weight_set(ad->rect, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(ad->rect, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_show(ad->rect);
+
+
+ /* Show window after base gui is set up */
+ evas_object_show(ad->win);
+}
+
+static const char *
+covert_camera_error_to_string(int error)
+{
+ switch (error) {
+ case CAMERA_ERROR_NONE:
+ return "NONE";
+ case CAMERA_ERROR_INVALID_PARAMETER:
+ return "INVALID_PARAMETER";
+ case CAMERA_ERROR_INVALID_STATE:
+ return "INVALID_STATE";
+ case CAMERA_ERROR_OUT_OF_MEMORY:
+ return " OUT_OF_MEMORY";
+ case CAMERA_ERROR_DEVICE:
+ return "DEVICE";
+ case CAMERA_ERROR_INVALID_OPERATION:
+ return "INVALID_OPERATION";
+ case CAMERA_ERROR_SECURITY_RESTRICTED:
+ return "SECURITY_RESTRICTED";
+ case CAMERA_ERROR_DEVICE_BUSY:
+ return "DEVICE_BUSY";
+ case CAMERA_ERROR_DEVICE_NOT_FOUND:
+ return "DEVICE_NOT_FOUND";
+ case CAMERA_ERROR_ESD:
+ return "ESD";
+ case CAMERA_ERROR_PERMISSION_DENIED:
+ return "PERMISSION_DENIED";
+ case CAMERA_ERROR_NOT_SUPPORTED:
+ return "NOT_SUPPORTED";
+ case CAMERA_ERROR_RESOURCE_CONFLICT:
+ return "RESOURCE_CONFLICT";
+ case CAMERA_ERROR_SERVICE_DISCONNECTED:
+ return "SERVICE_DISCONNECTED";
+ }
+ return "UNKNOWN";
+}
+
+static void
+start_preview(appdata_s *ad)
+{
+ int ret = camera_start_preview(ad->camera);
+ LOGD("ERROR: %s", covert_camera_error_to_string(ret));
+ RET_IF_ERROR(ret, "camera_start_preview");
+
+ LOGD("Started preview");
+}
+
+static bool
+__preview_resolution_cb(int width, int height, void *user_data)
+{
+ int *resolution = (int *)user_data;
+ resolution[0] = width;
+ resolution[1] = height;
+ return false;
+}
+
+static void
+__camera_capturing_cb(camera_image_data_s *image,
+ camera_image_data_s *postview,
+ camera_image_data_s *thumbnail,
+ void *user_data)
+{
+ LOGD("Capturing..");
+ appdata_s *ad = user_data;
+ if (image != NULL && image->data != NULL) {
+ char filename[150];
+ snprintf(filename, 150, "%s/%ld.jpg", ad->path, time(NULL));
+ filename[149] = '\0';
+ FILE *file = fopen(filename, "w+");
+ if (file == NULL) {
+ LOGE("Fail to open %s", filename);
+ return;
+ }
+ fwrite(image->data, 1, image->size, file);
+ fclose(file);
+ LOGD("Save image to %s", filename);
+ } else {
+ LOGE("Image is NULL");
+ }
+}
+
+static void
+__camera_completed_cb(void *user_data)
+{
+ LOGD("Captured");
+ appdata_s *ad = user_data;
+ usleep(25000);
+ start_preview(ad);
+}
+
+static void
+__camera_focus_cb(camera_focus_state_e state, void *user_data)
+{
+ if (state == CAMERA_FOCUS_STATE_FOCUSED) {
+ appdata_s *ad = user_data;
+ int ret = camera_start_capture(ad->camera, __camera_capturing_cb, __camera_completed_cb, ad);
+ RET_IF_ERROR(ret, "camera_start_capture");
+ }
+}
+
+static void
+set_camera_attributes(appdata_s *ad)
+{
+ camera_state_e state;
+ int ret = camera_get_state(ad->camera, &state);
+ RET_IF_ERROR(ret, "camera_get_state");
+ if (state != CAMERA_STATE_CREATED) {
+ LOGE("Camera is not created");
+ return;
+ }
+ LOGD("camera state %d", state);
+
+ ret = camera_attr_set_image_quality(ad->camera, 100);
+ RET_IF_ERROR(ret, "camera_attr_set_image_quality");
+
+ ret = camera_set_capture_format(ad->camera, CAMERA_PIXEL_FORMAT_JPEG);
+ RET_IF_ERROR(ret, "camera_set_capture_format");
+
+ ret = camera_set_display(ad->camera, CAMERA_DISPLAY_TYPE_OVERLAY, GET_DISPLAY(ad->win));
+ RET_IF_ERROR(ret, "camera_set_display");
+
+ int resolution[2];
+ ret = camera_foreach_supported_preview_resolution(ad->camera, __preview_resolution_cb, resolution);
+ RET_IF_ERROR(ret, "camera_foreach_supported_preview_resolution");
+
+ ret = camera_set_preview_resolution(ad->camera, resolution[0], resolution[1]);
+ RET_IF_ERROR(ret, "camera_set_preview_reolution");
+
+ ret = camera_set_focus_changed_cb(ad->camera, __camera_focus_cb, ad);
+ RET_IF_ERROR(ret, "camera_set_focus_changed_cb");
+}
+
+static bool
+__storage_cb(int storage_id, storage_type_e type, storage_state_e state,
+ const char *path, void *user_data)
+{
+ appdata_s *ad = (appdata_s *)user_data;
+ if (type == STORAGE_TYPE_INTERNAL) {
+ int ret = storage_get_root_directory(storage_id, &ad->path);
+ if (ret != STORAGE_ERROR_NONE)
+ LOGE("storage_get_root_directory failed %d", ret);
+ return false;
+ }
+ return true;
+}
+
+static void
+set_camera_storage(appdata_s *ad)
+{
+ int ret = storage_foreach_device_supported(__storage_cb, ad);
+ RET_IF_ERROR(ret, "storage_foreach_device_supported");
+
+ LOGD("Path: %s", ad->path);
+}
+
+void
+capture_image(void *user_data)
+{
+ appdata_s *ad = user_data;
+
+ LOGD("Capture Image");
+ int ret = camera_start_focusing(ad->camera, false);
+ if (ret == CAMERA_ERROR_NOT_SUPPORTED) {
+ ret = camera_start_capture(ad->camera, __camera_capturing_cb, __camera_completed_cb, ad);
+ RET_IF_ERROR(ret, "camera_start_capture");
+ return;
+ }
+ RET_IF_ERROR(ret, "camera_start_focusing");
+}
+
+static bool
+app_create(void *data)
+{
+ /* Hook to take necessary actions before main event loop starts
+ Initialize UI resources and application's data
+ If this function returns true, the main loop of application starts
+ If this function returns false, the application is terminated */
+ appdata_s *ad = data;
+
+
+ int ret = camera_create(CAMERA_DEVICE_CAMERA0, &(ad->camera));
+ if (ret != CAMERA_ERROR_NONE) {
+ LOGE("camera_create %d", ret);
+ return false;
+ }
+
+ create_base_gui(ad);
+ set_camera_attributes(ad);
+ set_camera_storage(ad);
+
+ start_preview(ad);
+
+ start_vine_server(ad);
+
+ return true;
+}
+
+static void
+app_control(app_control_h app_control, void *data)
+{
+ /* Handle the launch request. */
+}
+
+static void
+app_pause(void *data)
+{
+ /* Take necessary actions when application becomes invisible. */
+}
+
+static void
+app_resume(void *data)
+{
+ /* Take necessary actions when application becomes visible. */
+}
+
+static void
+app_terminate(void *data)
+{
+ /* Release all resources. */
+}
+
+static void
+ui_app_lang_changed(app_event_info_h event_info, void *user_data)
+{
+ /*APP_EVENT_LANGUAGE_CHANGED*/
+
+ int ret;
+ char *language;
+
+ ret = app_event_get_language(event_info, &language);
+ if (ret != APP_ERROR_NONE) {
+ dlog_print(DLOG_ERROR, LOG_TAG, "app_event_get_language() failed. Err = %d.", ret);
+ return;
+ }
+
+ if (language != NULL) {
+ elm_language_set(language);
+ free(language);
+ }
+}
+
+static void
+ui_app_orient_changed(app_event_info_h event_info, void *user_data)
+{
+ /*APP_EVENT_DEVICE_ORIENTATION_CHANGED*/
+ return;
+}
+
+static void
+ui_app_region_changed(app_event_info_h event_info, void *user_data)
+{
+ /*APP_EVENT_REGION_FORMAT_CHANGED*/
+}
+
+static void
+ui_app_low_battery(app_event_info_h event_info, void *user_data)
+{
+ /*APP_EVENT_LOW_BATTERY*/
+}
+
+static void
+ui_app_low_memory(app_event_info_h event_info, void *user_data)
+{
+ /*APP_EVENT_LOW_MEMORY*/
+}
+
+int
+main(int argc, char *argv[])
+{
+ appdata_s ad = {0,};
+ int ret = 0;
+
+ ui_app_lifecycle_callback_s event_callback = {0,};
+ app_event_handler_h handlers[5] = {NULL, };
+
+ event_callback.create = app_create;
+ event_callback.terminate = app_terminate;
+ event_callback.pause = app_pause;
+ event_callback.resume = app_resume;
+ event_callback.app_control = app_control;
+
+ ui_app_add_event_handler(&handlers[APP_EVENT_LOW_BATTERY], APP_EVENT_LOW_BATTERY, ui_app_low_battery, &ad);
+ ui_app_add_event_handler(&handlers[APP_EVENT_LOW_MEMORY], APP_EVENT_LOW_MEMORY, ui_app_low_memory, &ad);
+ ui_app_add_event_handler(&handlers[APP_EVENT_DEVICE_ORIENTATION_CHANGED], APP_EVENT_DEVICE_ORIENTATION_CHANGED, ui_app_orient_changed, &ad);
+ ui_app_add_event_handler(&handlers[APP_EVENT_LANGUAGE_CHANGED], APP_EVENT_LANGUAGE_CHANGED, ui_app_lang_changed, &ad);
+ ui_app_add_event_handler(&handlers[APP_EVENT_REGION_FORMAT_CHANGED], APP_EVENT_REGION_FORMAT_CHANGED, ui_app_region_changed, &ad);
+
+ ret = ui_app_main(argc, argv, &event_callback, &ad);
+ if (ret != APP_ERROR_NONE) {
+ dlog_print(DLOG_ERROR, LOG_TAG, "app_main() is failed. err = %d", ret);
+ }
+
+ return ret;
+}
--- /dev/null
+#include <vine.h>
+
+#include "remote-camera.h"
+#include "vine-server.h"
+
+#define SERVICE_TYPE "_vine._tcp"
+#define SERVICE_NAME "REMOTE_CAMERA"
+#define PORT 1234
+
+#define SHUTTER_OPERATION "REMOTE_CAMERA_SHUTTER"
+
+//capture_image();
+
+static vine_service_h __service;
+static vine_session_h __session;
+
+static void __set_service()
+{
+ int ret = vine_service_create(&__service);
+ RET_IF_ERROR(ret, "vine_service_create");
+
+ ret = vine_service_set_type(__service, SERVICE_TYPE);
+ RET_IF_ERROR(ret, "vine_service_set_type");
+
+ ret = vine_service_set_name(__service, SERVICE_NAME);
+ RET_IF_ERROR(ret, "vine_service_set_name");
+
+ ret = vine_service_set_port(__service, PORT);
+ RET_IF_ERROR(ret, "vine_service_set_port");
+}
+
+static void __published_cb(vine_session_h session,
+ const char *service_name, vine_error_e error, void *user_data)
+{
+ LOGD("%s is published", service_name);
+}
+
+static void __received_cb(vine_data_path_h datapath, size_t received_len, void *user_data)
+{
+ LOGD("Received a message");
+
+ unsigned char buf[200] = {0, };
+ size_t bytes = 0;
+
+ vine_data_path_read(datapath, buf, 200, &bytes);
+
+ if (memcmp(SHUTTER_OPERATION, buf, bytes) == 0) {
+ LOGD("Shutter");
+ capture_image(user_data);
+ } else {
+ LOGD("Unknown message %s", buf);
+ }
+}
+
+static void __connected_cb(vine_session_h session, vine_data_path_h datapath, void *user_data)
+{
+ LOGD("New data path");
+ int ret = vine_data_path_set_received_cb(datapath, __received_cb, user_data);
+ RET_IF_ERROR(ret, "vine_data_path_set_received_cb");
+}
+
+static void __start_server(void *data)
+{
+ LOGD("Start server");
+ vine_security_h security;
+ int ret = vine_security_create(&security);
+ RET_IF_ERROR(ret, "vine_security_create");
+
+ ret = vine_security_set_types(security, VINE_SECURITY_TYPE_NONE);
+ RET_IF_ERROR(ret, "vine_security_set_types");
+
+ ret = vine_session_set_connected_cb(__session, __connected_cb, data);
+ RET_IF_ERROR(ret, "vine_session_set_connected_cb");
+
+ ret = vine_session_listen(__session, PORT, security);
+ RET_IF_ERROR(ret, "vine_session_listen");
+
+ ret = vine_security_destroy(security);
+ RET_IF_ERROR(ret, "vine_security_destroy");
+
+}
+
+static void __register_service()
+{
+ int ret = vine_session_create(&__session);
+ RET_IF_ERROR(ret, "vine_service_create");
+
+ ret = vine_session_set_registered_cb(__session, __published_cb, NULL);
+ RET_IF_ERROR(ret, "vine_session_set_registered_cb");
+
+ ret = vine_session_register(__session, __service);
+ RET_IF_ERROR(ret, "vine_session_register");
+}
+
+static Eina_Bool
+__process_event(void *user_data, Ecore_Fd_Handler *fd_handler)
+{
+ LOGD("Process a vine event");
+
+ int ret = vine_process_event();
+ if (ret != VINE_ERROR_NONE) {
+ LOGE("vine_process_event %d", ret);
+ }
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static void __add_event_handler(void *data)
+{
+ int fd;
+ int ret = vine_get_event_fd(&fd);
+ RET_IF_ERROR(ret, "vine_get_event_fd");
+
+ ecore_main_fd_handler_add(fd, ECORE_FD_READ, __process_event, NULL, NULL, NULL);
+}
+
+void start_vine_server(void *data)
+{
+ LOGD("start_vine_server");
+ int ret = vine_initialize();
+ RET_IF_ERROR(ret, "vine_initialize()");
+
+ __add_event_handler(data);
+
+ __set_service();
+
+ __register_service();
+
+ __start_server(data);
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<manifest xmlns="http://tizen.org/ns/packages" api-version="2.4" package="org.example.remote-camera" version="1.0.0">
+ <profile name="mobile"/>
+ <ui-application appid="org.example.remote-camera" exec="remote-camera" launch_mode="single" multiple="false" nodisplay="false" taskmanage="true" type="capp">
+ <label>remote-camera</label>
+ <icon>remote-camera.png</icon>
+ </ui-application>
+ <privileges>
+ <privilege>http://tizen.org/privilege/mediastorage</privilege>
+ <privilege>http://tizen.org/privilege/internet</privilege>
+ <privilege>http://tizen.org/privilege/externalstorage</privilege>
+ <privilege>http://tizen.org/privilege/camera</privilege>
+ </privileges>
+</manifest>
--- /dev/null
+{
+ global: main;
+ _IO_*;
+ local: *;
+};
--- /dev/null
+bin/taptap-shutter__DEL__2S8QvYBNUeo/nZmmP1YRSGQj9b1KZKmjuvf+tUxcpcqf9xSZiY3Mp/aEDz8JyDJqfmPlWxEeiBS/
+1iulRyc5Ww==
+res/shutter.png__DEL__sxI0DT0FWSg6tGgblhPxWUy4YDq7OHe8N99hBnhupH8SWxS4LcCSllg8ceQfIu1urb+/4Kund9Th
+p+uQFXyvZQ==
+shared/res/taptap-shutter.png__DEL__BTGBG483ma/3HgWnreWF2xbdzr4F0gTRWjwxoSV+7kKBWt4UJTuv5WE4DhKdU4in2kwn/55O/qPD
+bNc/wgCuGg==
+tizen-manifest.xml__DEL__IOAyIL9wJpgRRnFYd3nuQCqK7DT/sCiTbEGC/y1iyhmt2ZckyciLM/HAHfYkNGbOr/VpgUexSvog
+q4TVr8kSHA==
+author-signature.xml__DEL__SrTt5jUh+GzqX3p92JLt1wEgFGTiytvZY00VQ5tHr8PM1uX7zO2zsVguDU3mVbis3TtJYBAkta2w
+QWgkmiaAqg==
--- /dev/null
+<Signature xmlns="http://www.w3.org/2000/09/xmldsig#" Id="AuthorSignature">
+<SignedInfo>
+<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod>
+<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"></SignatureMethod>
+<Reference URI="bin%2Ftaptap-shutter">
+<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
+<DigestValue>2S8QvYBNUeo/nZmmP1YRSGQj9b1KZKmjuvf+tUxcpcqf9xSZiY3Mp/aEDz8JyDJqfmPlWxEeiBS/
+1iulRyc5Ww==</DigestValue>
+</Reference>
+<Reference URI="res%2Fshutter.png">
+<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
+<DigestValue>sxI0DT0FWSg6tGgblhPxWUy4YDq7OHe8N99hBnhupH8SWxS4LcCSllg8ceQfIu1urb+/4Kund9Th
+p+uQFXyvZQ==</DigestValue>
+</Reference>
+<Reference URI="shared%2Fres%2Ftaptap-shutter.png">
+<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
+<DigestValue>BTGBG483ma/3HgWnreWF2xbdzr4F0gTRWjwxoSV+7kKBWt4UJTuv5WE4DhKdU4in2kwn/55O/qPD
+bNc/wgCuGg==</DigestValue>
+</Reference>
+<Reference URI="tizen-manifest.xml">
+<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
+<DigestValue>IOAyIL9wJpgRRnFYd3nuQCqK7DT/sCiTbEGC/y1iyhmt2ZckyciLM/HAHfYkNGbOr/VpgUexSvog
+q4TVr8kSHA==</DigestValue>
+</Reference>
+<Reference URI="#prop">
+<Transforms>
+<Transform Algorithm="http://www.w3.org/2006/12/xml-c14n11"></Transform>
+</Transforms>
+<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
+<DigestValue>aXbSAVgmAz0GsBUeZ1UmNDRrxkWhDUVGb45dZcNRq429wX3X+x6kaXT3NdNDTSNVTU+ypkysPMGv
+QY10fG1EWQ==</DigestValue>
+</Reference>
+</SignedInfo>
+<SignatureValue>
+UcvbX1Zt8fKKy6nBawYAE+gImxnOXpnzyBQnto5rSGvwyUoPI7MR6WTDZQgf6hgGiGP8CBciQ3t2
+hTqd66NosazMfKTrDtLmb2oDMbs6LwIpFf7DYSOfkj0VONM283Nizjk4nggsWegomR6+IbK25MFK
+xUPF+GrVmtuxAK+Tkbg=
+</SignatureValue>
+<KeyInfo>
+<X509Data>
+<X509Certificate>
+MIICmzCCAYOgAwIBAgIGAT8T+Ms5MA0GCSqGSIb3DQEBBQUAMFYxGjAYBgNVBAoMEVRpemVuIEFz
+c29jaWF0aW9uMRowGAYDVQQLDBFUaXplbiBBc3NvY2lhdGlvbjEcMBoGA1UEAwwTVGl6ZW4gRGV2
+ZWxvcGVycyBDQTAeFw0xMjExMDEwMDAwMDBaFw0xOTAxMDEwMDAwMDBaMBcxFTATBgNVBAMMDHRp
+emVuX2F1dGhvcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAssi+tZOAmBSLiy8JTOk7gXrk
+Nlg8Jq+O+4yd14a407BTbmp9r4qfNlUKLpYRL0TyU84XUWrLHHXe9aO2G223v/DUz3f182W5JWK0
+HvHt0vYcBycOBkXCc5y26funq7qhEgC9e1NNxIc1mFYD0jJiRNJgxIRCw33Ca/8qdGPY1K8CAwEA
+AaMyMDAwDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwDQYJ
+KoZIhvcNAQEFBQADggEBAJweqlVjN2GHF01jlE1HfZNFJ5Y7Ye+NagJnNdj6iVOPGP9J9PVOmllB
+8S8pon1fUXPvrHFe8gNFg1klLMJaPbeDQM+UiPWn80A+8e4gJpHz+E9on2ZYkEBCZHOXSxxzaS7D
+0bwj3eotbpY/duepn3U91fcLcnoehO3oe4pawrC6AObRjqY/Sh9I4j8btHFxRRVpsa7VkSg/5JB4
+2jfKlJ2otI8bYYuOiYeZC2ody9PsHzlqmqlo+J68RlxAemPMovRqGeK0XDqJEt7dJEzknQmp0732
+fhAZNyXVlukjdR8+3+f1J5vu4MEqT/7aLXLOwLN/qgUfCBA2YQ/YqMJvf9Y=
+</X509Certificate>
+<X509Certificate>
+MIIDOTCCAiGgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMRowGAYDVQQKDBFUaXplbiBBc3NvY2lh
+dGlvbjEaMBgGA1UECwwRVGl6ZW4gQXNzb2NpYXRpb24xHjAcBgNVBAMMFVRpemVuIERldmVsb3Bl
+cnMgUm9vdDAeFw0xMjAxMDEwMDAwMDBaFw0yNzAxMDEwMDAwMDBaMFYxGjAYBgNVBAoMEVRpemVu
+IEFzc29jaWF0aW9uMRowGAYDVQQLDBFUaXplbiBBc3NvY2lhdGlvbjEcMBoGA1UEAwwTVGl6ZW4g
+RGV2ZWxvcGVycyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANVGhRGmMIUyBA7o
+PCz8Sxut6z6HNkF4oDIuzuKaMzRYPeWodwe9O0gmqAkToQHfwg2giRhE5GoPld0fq+OYMMwSasCu
+g8dwODx1eDeSYVuOLWRxpAmbTXOsSFi6VoWeyaPEm18JBHvZBsU5YQtgZ6Kp7MqzvQg3pXOxtajj
+vyHxiatJl+xXrHgcXC1wgyG3buty7u/Fi2mvKXJ0PRJcCjjK81dqe/Vr20sRUCrbk02zbm5ggFt/
+jIEhV8wbFRQpliobc7J4dSTKhFfrqGM8rdd54LYhD7gSI1CFSe16pUXfcVR7FhJztRaiGLnCrwBE
+dyTZ248+D4L/qR/D0axb3jcCAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOC
+AQEAnOXXQ/1O/QTDHyrmQDtFziqPY3xWlJBqJtEqXiT7Y+Ljpe66e+Ee/OjQMlZe8gu21/8cKklH
+95RxjopMWCVedXDUbWdvS2+CdyvVW/quT2E0tjqIzXDekUTYwwhlPWlGxvfj3VsxqSFq3p8Brl04
+1Gx5RKAGyKVsMfTLhbbwSWwApuBUxYfcNpKwLWGPXkysu+HctY03OKv4/xKBnVWiN8ex/Sgesi0M
++OBAOMdZMPK32uJBTeKFx1xZgTLIhk45V0hPOomPjZloiv0LSS11eyd451ufjW0iHRE7WlpR6EvI
+W6TFyZgMpQq+kg4hWl2SBTf3s2VI8Ygz7gj8TMlClg==
+</X509Certificate>
+</X509Data>
+</KeyInfo>
+<Object Id="prop"><SignatureProperties xmlns:dsp="http://www.w3.org/2009/xmldsig-properties"><SignatureProperty Id="profile" Target="#AuthorSignature"><dsp:Profile URI="http://www.w3.org/ns/widgets-digsig#profile"></dsp:Profile></SignatureProperty><SignatureProperty Id="role" Target="#AuthorSignature"><dsp:Role URI="http://www.w3.org/ns/widgets-digsig#role-author"></dsp:Role></SignatureProperty><SignatureProperty Id="identifier" Target="#AuthorSignature"><dsp:Identifier></dsp:Identifier></SignatureProperty></SignatureProperties></Object>
+</Signature>
\ No newline at end of file
--- /dev/null
+<Signature xmlns="http://www.w3.org/2000/09/xmldsig#" Id="DistributorSignature">
+<SignedInfo>
+<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod>
+<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"></SignatureMethod>
+<Reference URI="author-signature.xml">
+<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
+<DigestValue>SrTt5jUh+GzqX3p92JLt1wEgFGTiytvZY00VQ5tHr8PM1uX7zO2zsVguDU3mVbis3TtJYBAkta2w
+QWgkmiaAqg==</DigestValue>
+</Reference>
+<Reference URI="bin%2Ftaptap-shutter">
+<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
+<DigestValue>2S8QvYBNUeo/nZmmP1YRSGQj9b1KZKmjuvf+tUxcpcqf9xSZiY3Mp/aEDz8JyDJqfmPlWxEeiBS/
+1iulRyc5Ww==</DigestValue>
+</Reference>
+<Reference URI="res%2Fshutter.png">
+<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
+<DigestValue>sxI0DT0FWSg6tGgblhPxWUy4YDq7OHe8N99hBnhupH8SWxS4LcCSllg8ceQfIu1urb+/4Kund9Th
+p+uQFXyvZQ==</DigestValue>
+</Reference>
+<Reference URI="shared%2Fres%2Ftaptap-shutter.png">
+<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
+<DigestValue>BTGBG483ma/3HgWnreWF2xbdzr4F0gTRWjwxoSV+7kKBWt4UJTuv5WE4DhKdU4in2kwn/55O/qPD
+bNc/wgCuGg==</DigestValue>
+</Reference>
+<Reference URI="tizen-manifest.xml">
+<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
+<DigestValue>IOAyIL9wJpgRRnFYd3nuQCqK7DT/sCiTbEGC/y1iyhmt2ZckyciLM/HAHfYkNGbOr/VpgUexSvog
+q4TVr8kSHA==</DigestValue>
+</Reference>
+<Reference URI="#prop">
+<Transforms>
+<Transform Algorithm="http://www.w3.org/2006/12/xml-c14n11"></Transform>
+</Transforms>
+<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
+<DigestValue>/r5npk2VVA46QFJnejgONBEh4BWtjrtu9x/IFeLksjWyGmB/cMWKSJWQl7aU3YRQRZ3AesG8gF7q
+GyvKX9Snig==</DigestValue>
+</Reference>
+</SignedInfo>
+<SignatureValue>
+Jr/VH+XZTjs4jWqp3Wf+B5BqiuoxXfhJbUkx8QSAfe5c49zS4fXxXgazn8I1es85K8ebtEjQe/6O
+gUUMngqN/zYRCQcqBGpsCtkqU2a/mob7JjZUPmMQkeV75TvTtYvLHbFVNAV8YWjSspPRRdhSbBYT
+22Tv/ex2+VvYyNGJz/o=
+</SignatureValue>
+<KeyInfo>
+<X509Data>
+<X509Certificate>
+MIICtzCCAiACCQCX7BAlwjllkTANBgkqhkiG9w0BAQUFADCBnTELMAkGA1UEBhMCS1IxDjAMBgNV
+BAgMBVN1d29uMQ4wDAYDVQQHDAVTdXdvbjEWMBQGA1UECgwNVGl6ZW4gVGVzdCBDQTEiMCAGA1UE
+CwwZVGl6ZW4gRGlzdHJpYnV0b3IgVGVzdCBDQTEyMDAGA1UEAwwpVGl6ZW4gUGFydG5lci1NYW51
+ZmFjdHVyZXIgRGlzdHJpYnV0b3IgQ0EwHhcNMTIxMjEzMDU0NDI3WhcNMjIxMjExMDU0NDI3WjCB
+oTELMAkGA1UEBhMCS1IxDjAMBgNVBAgMBVN1d29uMQ4wDAYDVQQHDAVTdXdvbjEWMBQGA1UECgwN
+VGl6ZW4gVGVzdCBDQTEiMCAGA1UECwwZVGl6ZW4gRGlzdHJpYnV0b3IgVGVzdCBDQTE2MDQGA1UE
+AwwtVGl6ZW4gUGFydG5lci1NYW51ZmFjdHVyZXIgRGlzdHJpYnV0b3IgU2lnbmVyMIGfMA0GCSqG
+SIb3DQEBAQUAA4GNADCBiQKBgQC2O0i2ou9Pa8KI+RVu6/1QAMichy8YqDtpM4cogiX+tYfafN/v
+4l7q9sQdotaFPLWPR1HNtG6O6SKWZmM/Hp2U+S3vDeylEhex/IopWF9RZMPJFyKr9bJnlzTqiZuz
+qItlXOZZBHhUL73v6OxqYg2BsA88jLtCoZWVAkoKDX93ZQIDAQABMA0GCSqGSIb3DQEBBQUAA4GB
+AJ5vn+dEhnZFuRdIMUt8TwQzXSxPMqNmR8+fcwwzAacxeOTgeK1C+DqE31vead/c8CVDMurZHQbs
+qH/Ala6FomTm1q0uuM3Ll2qZaPkI3qr+h37YD4AI5scyIlP7ppApcdPRSkDgqfXkika62Q9M0+MN
+nxl8+AvEukgC6qcd90oL
+</X509Certificate>
+<X509Certificate>
+MIICzzCCAjigAwIBAgIJAMNp1nbwir4GMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYDVQQGEwJLUjEO
+MAwGA1UECAwFU3V3b24xDjAMBgNVBAcMBVN1d29uMRYwFAYDVQQKDA1UaXplbiBUZXN0IENBMSIw
+IAYDVQQLDBlUaXplbiBEaXN0cmlidXRvciBUZXN0IENBMTcwNQYDVQQDDC5UaXplbiBQYXJ0bmVy
+LU1hbnVmYWN0dXJlciBEaXN0cmlidXRvciBSb290IENBMB4XDTEyMTIxMzA1NDQyMFoXDTIyMTIx
+MTA1NDQyMFowgZ0xCzAJBgNVBAYTAktSMQ4wDAYDVQQIDAVTdXdvbjEOMAwGA1UEBwwFU3V3b24x
+FjAUBgNVBAoMDVRpemVuIFRlc3QgQ0ExIjAgBgNVBAsMGVRpemVuIERpc3RyaWJ1dG9yIFRlc3Qg
+Q0ExMjAwBgNVBAMMKVRpemVuIFBhcnRuZXItTWFudWZhY3R1cmVyIERpc3RyaWJ1dG9yIENBMIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMni4Qmv96ww/De92kYhtzpas7viwyYoFTTuvcmayD
+FBuDoaHqrSDsrb9M7qrR4N9higg1mT8a9J8CUhPLKhlifK9OwyQsZS+8xVfPfogIJ3wumfjLl5zi
+nqpkSe2ernxOTU+PB5lWU8toKbnJO3+jchm14DjCQ4ZoqG26qbG5pQIDAQABoxAwDjAMBgNVHRME
+BTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAA9lsDyArDR9balTfqrjN241nVqyLqPOP+MFRjGWrIlI
+yLDcw2/L9hxguD8XhgAanaQUzS06XM6BajDjcSiJrohjoMFkB09M+r5fVkrhkT29oABsKXO87+p8
+JbbbIfdwMUrAjXsrjMUzJ3J6fn86/dn/PcuMAkCgrD069IzpE8NM
+</X509Certificate>
+</X509Data>
+</KeyInfo>
+<Object Id="prop"><SignatureProperties xmlns:dsp="http://www.w3.org/2009/xmldsig-properties"><SignatureProperty Id="profile" Target="#DistributorSignature"><dsp:Profile URI="http://www.w3.org/ns/widgets-digsig#profile"></dsp:Profile></SignatureProperty><SignatureProperty Id="role" Target="#DistributorSignature"><dsp:Role URI="http://www.w3.org/ns/widgets-digsig#role-distributor"></dsp:Role></SignatureProperty><SignatureProperty Id="identifier" Target="#DistributorSignature"><dsp:Identifier></dsp:Identifier></SignatureProperty></SignatureProperties></Object>
+</Signature>
\ No newline at end of file
--- /dev/null
+#!/bin/bash
+ROOTSTRAP="mobile-6.0-device.core"
+
+function clean {
+ echo "Clean build directory"
+ rm -rf Debug
+}
+
+function pack {
+ echo "Packaging.."
+ tizen package -t tpk -- ./Debug
+}
+
+function build {
+ echo "Build.."
+ tizen build-native -r $ROOTSTRAP -a arm -C Debug -- .
+}
+
+if [ $# -eq 1 ]
+then
+case $1 in
+ "clean")
+ clean
+ ;;
+ "build")
+ build
+ ;;
+ "pack")
+ pack
+ ;;
+esac
+fi
+
--- /dev/null
+#ifndef __taptap_shutter_H__
+#define __taptap_shutter_H__
+
+#include <app.h>
+#include <Elementary.h>
+#include <system_settings.h>
+#include <efl_extension.h>
+#include <dlog.h>
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "taptap-shutter"
+
+#if !defined(PACKAGE)
+#define PACKAGE "org.example.taptap-shutter"
+#endif
+
+#define LOGD(fmt, args...) dlog_print(DLOG_DEBUG, LOG_TAG, fmt, ##args)
+#define LOGE(fmt, args...) dlog_print(DLOG_ERROR, LOG_TAG, fmt, ##args)
+
+#define RET_IF_ERROR(fun_name, error_code) do {\
+ if (error_code != 0) { \
+ LOGE("%s %d", fun_name, error_code); \
+ return; \
+ } \
+ } while (0)
+
+#endif /* __taptap_shutter_H__ */
--- /dev/null
+#ifndef VINE_CLIENT_H
+#define VINE_CLIENT_H
+
+typedef void(*found_cb)(const char *name, void *data);
+typedef void(*connected_cb)(void *data);
+
+void start_vine_client(found_cb fcb, connected_cb ccb, void *data);
+void shutter();
+
+#endif
--- /dev/null
+APPNAME = taptap-shutter
+
+type = app
+profile = wearable-6.0
+
+USER_SRCS = src/*.c
+USER_DEFS =
+USER_INC_DIRS = inc
+USER_OBJS =
+USER_LIBS =
+USER_EDCS =
--- /dev/null
+#include "taptap-shutter.h"
+#include "vine-client.h"
+
+typedef struct appdata {
+ Evas_Object *win;
+ Evas_Object *conform;
+ Evas_Object *shutter_btn;
+} appdata_s;
+
+static void
+win_delete_request_cb(void *data, Evas_Object *obj, void *event_info)
+{
+ ui_app_exit();
+}
+
+static void
+win_back_cb(void *data, Evas_Object *obj, void *event_info)
+{
+ appdata_s *ad = data;
+ /* Let window go to hide state. */
+ elm_win_lower(ad->win);
+}
+
+static void
+__clicked_cb(void *data, Evas_Object *obj, void *event_info)
+{
+ LOGD("Clicked");
+ shutter();
+}
+
+static void
+create_base_gui(appdata_s *ad)
+{
+ /* Window */
+ /* Create and initialize elm_win.
+ elm_win is mandatory to manipulate window. */
+ ad->win = elm_win_util_standard_add(PACKAGE, PACKAGE);
+ elm_win_autodel_set(ad->win, EINA_TRUE);
+
+ if (elm_win_wm_rotation_supported_get(ad->win)) {
+ int rots[4] = { 0, 90, 180, 270 };
+ elm_win_wm_rotation_available_rotations_set(ad->win, (const int *)(&rots), 4);
+ }
+
+ evas_object_smart_callback_add(ad->win, "delete,request", win_delete_request_cb, NULL);
+ eext_object_event_callback_add(ad->win, EEXT_CALLBACK_BACK, win_back_cb, ad);
+
+ /* Conformant */
+ /* Create and initialize elm_conformant.
+ elm_conformant is mandatShutterory for base gui to have proper size
+ when indicator or virtual keypad is visible. */
+ ad->conform = elm_conformant_add(ad->win);
+ elm_win_indicator_mode_set(ad->win, ELM_WIN_INDICATOR_SHOW);
+ elm_win_indicator_opacity_set(ad->win, ELM_WIN_INDICATOR_OPAQUE);
+ evas_object_size_hint_weight_set(ad->conform, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ elm_win_resize_object_add(ad->win, ad->conform);
+ evas_object_show(ad->conform);
+
+ /* Label */
+ /* Create an actual view of the base gui.
+ Modify this part to change the view. */
+ ad->shutter_btn = elm_button_add(ad->conform);
+ elm_object_text_set(ad->shutter_btn, "<align=center>Connecting</align>");
+ //evas_object_size_hint_weight_set(ad->label, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ //elm_object_style_set(ad->shutter_btn, "nextdepth")
+ //elm_object_disabled_set(ad->shutter_btn, EINA_TRUE);
+
+ /*
+ Evas_Object *ic = elm_icon_add(ad->shutter_btn);
+ elm_image_file_set(ic, "shutter.png", NULL);
+ elm_object_part_content_set(ad->shutter_btn, "icon", ic);
+ */
+
+ elm_object_content_set(ad->conform, ad->shutter_btn);
+
+ evas_object_smart_callback_add(ad->shutter_btn, "clicked", __clicked_cb, NULL);
+
+ /* Show window after base gui is set up */
+ evas_object_show(ad->win);
+}
+
+static void __found_cb(const char *name, void *data)
+{
+ appdata_s *ad = data;
+ char str[1024];
+ snprintf(str, 1024, "<align=center>%s</align>", name);
+ str[1023] = '\0';
+ elm_object_text_set(ad->shutter_btn, str);
+}
+
+static void __connected_cb(void *data)
+{
+ appdata_s *ad = data;
+ elm_object_text_set(ad->shutter_btn, "Connected");
+ elm_object_disabled_set(ad->shutter_btn, EINA_FALSE);
+}
+
+static bool
+app_create(void *data)
+{
+ /* Hook to take necessary actions before main event loop starts
+ Initialize UI resources and application's data
+ If this function returns true, the main loop of application starts
+ If this function returns false, the application is terminated */
+ appdata_s *ad = data;
+
+ create_base_gui(ad);
+
+ start_vine_client(__found_cb, __connected_cb, ad);
+
+ return true;
+}
+
+static void
+app_control(app_control_h app_control, void *data)
+{
+ /* Handle the launch request. */
+}
+
+static void
+app_pause(void *data)
+{
+ /* Take necessary actions when application becomes invisible. */
+}
+
+static void
+app_resume(void *data)
+{
+ /* Take necessary actions when application becomes visible. */
+}
+
+static void
+app_terminate(void *data)
+{
+ /* Release all resources. */
+}
+
+static void
+ui_app_lang_changed(app_event_info_h event_info, void *user_data)
+{
+ /*APP_EVENT_LANGUAGE_CHANGED*/
+ char *locale = NULL;
+ system_settings_get_value_string(SYSTEM_SETTINGS_KEY_LOCALE_LANGUAGE, &locale);
+ elm_language_set(locale);
+ free(locale);
+ return;
+}
+
+static void
+ui_app_orient_changed(app_event_info_h event_info, void *user_data)
+{
+ /*APP_EVENT_DEVICE_ORIENTATION_CHANGED*/
+ return;
+}
+
+static void
+ui_app_region_changed(app_event_info_h event_info, void *user_data)
+{
+ /*APP_EVENT_REGION_FORMAT_CHANGED*/
+}
+
+static void
+ui_app_low_battery(app_event_info_h event_info, void *user_data)
+{
+ /*APP_EVENT_LOW_BATTERY*/
+}
+
+static void
+ui_app_low_memory(app_event_info_h event_info, void *user_data)
+{
+ /*APP_EVENT_LOW_MEMORY*/
+}
+
+int
+main(int argc, char *argv[])
+{
+ appdata_s ad = {0,};
+ int ret = 0;
+
+ ui_app_lifecycle_callback_s event_callback = {0,};
+ app_event_handler_h handlers[5] = {NULL, };
+
+ event_callback.create = app_create;
+ event_callback.terminate = app_terminate;
+ event_callback.pause = app_pause;
+ event_callback.resume = app_resume;
+ event_callback.app_control = app_control;
+
+ ui_app_add_event_handler(&handlers[APP_EVENT_LOW_BATTERY], APP_EVENT_LOW_BATTERY, ui_app_low_battery, &ad);
+ ui_app_add_event_handler(&handlers[APP_EVENT_LOW_MEMORY], APP_EVENT_LOW_MEMORY, ui_app_low_memory, &ad);
+ ui_app_add_event_handler(&handlers[APP_EVENT_DEVICE_ORIENTATION_CHANGED], APP_EVENT_DEVICE_ORIENTATION_CHANGED, ui_app_orient_changed, &ad);
+ ui_app_add_event_handler(&handlers[APP_EVENT_LANGUAGE_CHANGED], APP_EVENT_LANGUAGE_CHANGED, ui_app_lang_changed, &ad);
+ ui_app_add_event_handler(&handlers[APP_EVENT_REGION_FORMAT_CHANGED], APP_EVENT_REGION_FORMAT_CHANGED, ui_app_region_changed, &ad);
+
+ ret = ui_app_main(argc, argv, &event_callback, &ad);
+ if (ret != APP_ERROR_NONE) {
+ dlog_print(DLOG_ERROR, LOG_TAG, "app_main() is failed. err = %d", ret);
+ }
+
+ return ret;
+}
--- /dev/null
+#include <vine.h>
+#include <dlog.h>
+
+#include "taptap-shutter.h"
+#include "vine-client.h"
+
+#define SERVICE_TYPE "_vine._tcp"
+#define SERVICE_NAME "REMOTE_CAMERA"
+#define MAX_VINE_MSG_LEN 255
+
+#define SHUTTER_OPERATION "REMOTE_CAMERA_SHUTTER"
+
+static vine_session_h __session;
+static vine_data_path_h __dp = NULL;
+
+static void *__app_data = NULL;
+
+static void __connect(char *ip, int port)
+{
+ LOGD("Try to connect a vine server");
+
+ vine_security_h security;
+ int ret = vine_security_create(&security);
+ RET_IF_ERROR("vine_security_create", ret);
+
+ ret = vine_security_set_types(security, VINE_SECURITY_TYPE_NONE);
+ RET_IF_ERROR("vine_security_set_types", ret);
+
+ ret = vine_session_connect(__session, ip, port, security);
+ RET_IF_ERROR("vine_session_connect", ret);
+
+ ret = vine_security_destroy(security);
+ RET_IF_ERROR("vine_security_destroy", ret);
+}
+
+static void __discovered_cb(vine_session_h session, vine_service_h service,
+ vine_service_state_e state, void *user_data)
+{
+ char *service_name;
+ int port;
+ char *ip;
+ vine_address_family_e address_family;
+ found_cb callback = (found_cb)user_data;
+
+ int ret = vine_service_get_name(service, &service_name);
+ RET_IF_ERROR("vine_service_get_name", ret);
+
+ ret = vine_service_get_ip(service, &address_family, &ip);
+ RET_IF_ERROR("vine_service_get_ip", ret);
+
+ ret = vine_service_get_port(service, &port);
+ RET_IF_ERROR("vine_service_get_port", ret);
+
+ if (state != VINE_SERVICE_AVAILABLE) {
+ LOGD("Unavailable Service");
+ LOGD("Service Name: %s", service_name);
+ return;
+ }
+
+ LOGD("Available Service");
+ LOGD("Service Name: %s", service_name);
+ LOGD("IP Address: %s", ip);
+ LOGD("Port: %d", port);
+
+ if (!strcmp(service_name, SERVICE_NAME)) {
+ __connect(ip, port);
+ }
+
+ if (callback)
+ callback(service_name, __app_data);
+}
+
+static void __connected_cb(vine_session_h session, vine_data_path_h datapath, void *user_data)
+{
+ connected_cb callback = (connected_cb)user_data;
+ LOGD("New data path");
+ __dp = datapath;
+
+ if (callback)
+ callback(__app_data);
+}
+
+static void __discover_service(found_cb callback)
+{
+ LOGD("Subscribe a vine service");
+
+ int ret = vine_session_create(&__session);
+ RET_IF_ERROR("vine_initialize", ret);
+
+ ret = vine_session_set_discovered_cb(__session, __discovered_cb, callback);
+ RET_IF_ERROR("vine_session_set_discovered_cb", ret);
+
+ ret = vine_session_start_discovery(__session, SERVICE_TYPE);
+ RET_IF_ERROR("vine_session_start_discovery", ret);
+}
+
+static Eina_Bool
+__process_event(void *user_data, Ecore_Fd_Handler *fd_handler)
+{
+ LOGD("Process a vine event");
+
+ int ret = vine_process_event();
+ if (ret != VINE_ERROR_NONE) {
+ LOGE("vine_process_event %d", ret);
+ }
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static void __add_event_handler()
+{
+ int fd;
+ int ret = vine_get_event_fd(&fd);
+ RET_IF_ERROR("vine_get_event_fd", ret);
+
+ ecore_main_fd_handler_add(fd, ECORE_FD_READ, __process_event, NULL, NULL, NULL);
+}
+
+void start_vine_client(found_cb fcb, connected_cb ccb, void *data)
+{
+ LOGD("Start vine client");
+
+ int ret = vine_initialize();
+ RET_IF_ERROR("vine_initialize", ret);
+
+ __add_event_handler();
+
+ __discover_service(fcb);
+
+ __app_data = data;
+ ret = vine_session_set_connected_cb(__session, __connected_cb, ccb);
+ RET_IF_ERROR("vine_session_set_connected_cb", ret);
+}
+
+static int __create_shutter_request(unsigned char buf[MAX_VINE_MSG_LEN])
+{
+ memcpy(buf, SHUTTER_OPERATION, strlen(SHUTTER_OPERATION));
+ return strlen(SHUTTER_OPERATION);
+}
+
+void shutter()
+{
+ if (__dp == NULL) {
+ //LOGD("Data path is not established yet");
+ return;
+ }
+
+ unsigned char msg[MAX_VINE_MSG_LEN];
+ int len = __create_shutter_request(msg);
+ int ret = vine_data_path_write(__dp, msg, len);
+ RET_IF_ERROR("vine_data_path_write", ret);
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<manifest xmlns="http://tizen.org/ns/packages" api-version="6.0" package="org.example.taptap-shutter" version="1.0.0">
+ <profile name="wearable"/>
+ <ui-application appid="org.example.taptap-shutter" exec="taptap-shutter" multiple="false" nodisplay="false" taskmanage="true" type="capp">
+ <label>taptap-shutter</label>
+ <icon>taptap-shutter.png</icon>
+ </ui-application>
+ <privileges>
+ <privilege>http://tizen.org/privilege/network.get</privilege>
+ <privilege>http://tizen.org/privilege/network.set</privilege>
+ <privilege>http://tizen.org/privilege/internet</privilege>
+ <privilege>http://tizen.org/privilege/network.profile</privilege>
+ </privileges>
+</manifest>
--- /dev/null
+# Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+INCLUDE_DIRECTORIES(
+ ${CMAKE_SOURCE_DIR}/include
+ ${VINE_PATH}/include
+ ${VINE_LOGGER_PATH}
+ ${fw_name_deps_INCLUDE_DIRS}
+)
+
+FILE(GLOB VINE_SOURCES ${VINE_PATH}/*.c ${VINE_PATH}/*.cpp)
+
+ADD_DEFINITIONS("-fvisibility=default")
+ADD_LIBRARY(${TARGET_VINE} SHARED ${VINE_SOURCES})
+
+SET_TARGET_PROPERTIES(
+ ${TARGET_VINE}
+ PROPERTIES
+ SOVERSION ${VINE_VERSION_MAJOR}
+ VERSION ${VINE_VERSION}
+)
+
+TARGET_LINK_LIBRARIES(${TARGET_VINE}
+ ${VINE_LOGGER}
+ ${fw_name_deps_LIBRARIES}
+ ${VINE_DEPS_LIB}
+ dl
+)
+
+INSTALL(TARGETS ${TARGET_VINE} DESTINATION "${LIB_DIR}")
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdio.h>
+
+enum {
+ AUTH_TYPE_OPEN = 0x00,
+ AUTH_TYPE_PSK = 0x01,
+};
+
+enum {
+ AUTH_OP_REQUEST = 0x01,
+ AUTH_OP_RESPONSE = 0x02,
+ AUTH_OP_CONFIRM = 0x04,
+};
+
+#define AUTH_VERSION 1
+
+unsigned char *alloc_auth_frame(unsigned char ver,
+ unsigned char auth_type, unsigned char op,
+ int data_len, const unsigned char *data, int *size);
+
+void release_auth_frame(unsigned char *buf);
+
+bool parse_auth_frame(unsigned char *buf, size_t buf_size,
+ unsigned char *ver, unsigned char *auth_type,
+ unsigned char *op, int *data_len, unsigned char **data);
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VINE_CONSTANTS_H__
+#define __VINE_CONSTANTS_H__
+
+#define VINE_NAMESPACE_BEGIN namespace vine {
+#define VINE_NAMESPACE_END }
+#define VINE_NAMESPACE_USE using namespace vine
+
+#define VINE_MAX_SERVICE_TYPE_LEN 63
+#define VINE_MAX_SERVICE_NAME_LEN 63
+#define VINE_MAX_HOST_NAME_LEN 255
+#define VINE_MAX_IP_LEN 39 // for IPv6
+#define VINE_MAX_KEY_LEN 9
+
+#define VINE_MAX_TOPIC_LEN 63
+
+#endif /* __VINE_SERV__VINE_CONSTANTS_H__ICE_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#ifndef __VINE_DATA_PATH_PLUGIN_H__
+#define __VINE_DATA_PATH_PLUGIN_H__
+
+#ifdef USE_LIBWEBSOCKETS
+#define LIBWEBSOCKETS_PLUGIN_PATH "libvine-plugin-libwebsockets.so"
+#define DATA_PATH_PLUGIN_PATH LIBWEBSOCKETS_PLUGIN_PATH
+#else
+#define DATA_PATH_PLUGIN_PATH ""
+#endif
+
+#define VINE_DATA_PATH_MAX_BUFFER_SIZE 131072 // 128 * 1024 bytes
+
+typedef void *vine_dp_plugin_h;
+
+typedef enum {
+ VINE_DATA_PATH_ERROR_NONE = 0,
+ VINE_DATA_PATH_ERROR_INVALID_PARAMETER,
+ VINE_DATA_PATH_ERROR_INVALID_OPERATION,
+ VINE_DATA_PATH_ERROR_OPERATION_FAILED,
+ VINE_DATA_PATH_ERROR_OUT_OF_MEMORY
+} vine_data_path_error;
+
+typedef enum {
+ VINE_DATA_PATH_POLLFD_ADD = 0,
+ VINE_DATA_PATH_POLLFD_DEL,
+ VINE_DATA_PATH_POLLFD_MOD,
+ VINE_DATA_PATH_POLLFD_OP,
+ VINE_DATA_PATH_POLLFD_LOCK_UNLOCK,
+} vine_data_path_pollfd_op_e;
+
+typedef enum {
+ VINE_DP_IPV4_IPV6 = 0, // IPv6 has higer priority.
+ VINE_DP_IPV4,
+ VINE_DP_IPV6,
+} vine_dp_addr_family_e;
+
+typedef enum {
+ VINE_DP_TLS_VERSION_DEFAULT = 0,
+ VINE_DP_TLS_VERSION_1_0 = 100,
+ VINE_DP_TLS_VERSION_1_1 = 101,
+ VINE_DP_TLS_VERSION_1_2 = 102,
+ VINE_DP_TLS_VERSION_1_3 = 103,
+
+ VINE_DP_TLS_VERSION_MAX = 999,
+} vine_dp_tls_version_e;
+
+typedef enum {
+ VINE_DATA_PATH_V_FLAG_DEFALT = 0,
+ VINE_DATA_PATH_V_FLAG_ALLOW_SS_CERTIFICATE = (1 << 0),
+ VINE_DATA_PATH_V_FLAG_SKIP_CN_CHECK = (1 << 1),
+} vine_dp_ssl_vflag_e;
+
+typedef struct {
+ bool use_ssl;
+ vine_dp_tls_version_e tls_version;
+ int vflags;
+ char *ca_path;
+ char *cert_path;
+ char *key_path;
+} vine_dp_ssl;
+
+typedef struct {
+ void (*pollfd_cb)(vine_data_path_pollfd_op_e op, int fd, int event);
+ void (*opened_cb)(int result, int port, void *user_data);
+ void (*accepted_cb)(char *addr, int port, void *plugin_data, void *user_data);
+ void (*connected_cb)(int result, void *user_data);
+ void (*received_cb)(size_t bytes, void *user);
+ void (*written_cb)(int bytes, void *user_data);
+ void (*terminated_cb)(void *user);
+} vine_dp_plugin_callbacks;
+
+typedef struct {
+ int (*init)(void);
+ void (*deinit)(void);
+ void (*register_callbacks)(vine_dp_plugin_callbacks callbacks);
+ void (*process_event)(int fd, int events);
+
+ int (*create)(vine_dp_plugin_h *handle, void *plugin_data, void *user);
+ int (*destroy)(vine_dp_plugin_h handle);
+ int (*open)(vine_dp_plugin_h handle, int addr_family,
+ int port, const char *iface_name, int max_conn, vine_dp_ssl ssl); // server only
+ int (*connect)(vine_dp_plugin_h handle, int addr_family,
+ const char *ip, int port, const char *iface_name, vine_dp_ssl ssl);
+ int (*read)(vine_dp_plugin_h handle, unsigned char *buf, size_t len);
+ int (*write)(vine_dp_plugin_h handle, unsigned char *buf, size_t len);
+ int (*close)(vine_dp_plugin_h handle);
+} vine_dp_plugin_fn;
+
+extern "C" void vine_data_path_plugin_init(vine_dp_plugin_fn *fn);
+
+#endif /* __VINE_DATA_PATH_PLUGIN_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "vine.h"
+#include "vine-data-path.h"
+#include "vine-data-path-plugin.h"
+#include "vine-timer.h"
+
+// fix build error.
+// It will be removed when vine-data-path-state is changed.
+typedef void *vine_data_path_h;
+
+#define AUTH_CONNECTED_TIMEOUT_MS 5000
+#define AUTH_REQUEST_TIMEOUT_MS 5000
+#define AUTH_RESPONSE_TIMEOUT_MS 5000
+
+class VineDataPathState
+{
+public:
+ VineDataPathState(vine_data_path_h dp, vine_dp_plugin_h plugin);
+ virtual ~VineDataPathState();
+
+ virtual void enter() = 0;
+ virtual void exit() = 0;
+
+ virtual int read(unsigned char *buf, size_t buf_len, size_t *read_len) = 0;
+ virtual int write(unsigned char *buf, size_t len) = 0;
+ virtual void received_cb(size_t bytes) = 0;
+
+ virtual void written_cb(size_t bytes) = 0;
+
+ virtual std::string str() = 0;
+
+protected:
+ vine_data_path_h __dp;
+ vine_dp_plugin_h __plugin;
+};
+
+// Vine Datapath is in the VineDefaultState when initialized
+class VineDefaultState : public VineDataPathState
+{
+public:
+ VineDefaultState(vine_data_path_h dp, vine_dp_plugin_h plugin);
+ ~VineDefaultState();
+
+ void enter();
+ void exit();
+
+ int read(unsigned char *buf, size_t buf_len, size_t *read_len);
+ int write(unsigned char *buf, size_t len);
+ void received_cb(size_t bytes);
+
+ void written_cb(size_t bytes);
+
+ std::string str() { return "Default State"; };
+};
+
+// Vine Datapath is set as VineAcceptedState when a websocket connection is accepted in server side.
+// In order to communicate with its peer,
+// an additional authentication process is also required.
+class VineAcceptedState : public VineDataPathState
+{
+public:
+ VineAcceptedState(vine_data_path_h dp, vine_dp_plugin_h plugin);
+ ~VineAcceptedState();
+
+ void enter();
+ void exit();
+
+ int read(unsigned char *buf, size_t buf_len, size_t *read_len);
+ int write(unsigned char *buf, size_t len);
+ void received_cb(size_t bytes);
+
+ void written_cb(size_t bytes);
+
+ std::string str() { return "Accepted State"; };
+
+private:
+ void handle_failure(std::string msg);
+ void send_auth_request();
+ void send_auth_confirm(int accept);
+
+ int __sent_frame;
+ int __accept;
+};
+
+// Vine Datapath is set as VineConnectedState when websocket connection is established in client side.
+// In order to communicate with its peer,
+// an additional authentication process is also required.
+class VineConnectedState : public VineDataPathState
+{
+public:
+ VineConnectedState(vine_data_path_h dp, vine_dp_plugin_h plugin);
+ ~VineConnectedState();
+
+ void enter();
+ void exit();
+
+ int read(unsigned char *buf, size_t buf_len, size_t *read_len);
+ int write(unsigned char *buf, size_t len);
+ void received_cb(size_t bytes);
+
+ void written_cb(size_t bytes);
+
+ std::string str() { return "Connected State"; };
+
+private:
+ void handle_failure(std::string msg);
+ void read_data(size_t bytes);
+ void recv_auth_request(unsigned char ver, unsigned char auth_type, unsigned char op,
+ int data_len, unsigned char *data);
+ void recv_auth_confirm(unsigned char ver, unsigned char auth_type, unsigned char op,
+ int data_len, unsigned char *data);
+ void send_auth_response();
+
+ static void __connected_timer(void *user_data);
+ vine::VineTimer __timer;
+
+ int __sent_frame;
+};
+
+// Vine Datapath is set as VineAuthRequestedState when AUTH_REQUEST message is sent.
+// It is only for Authenticate Initiator, which is usually the server (publisher).
+// On receiving a valid AUTH_RESPONSE, it is changed to VineAuthCompletedState.
+// On some failures, it is changed to VineAuthFailedState.
+class VineAuthRequestedState : public VineDataPathState
+{
+public:
+ VineAuthRequestedState(vine_data_path_h dp, vine_dp_plugin_h plugin);
+ ~VineAuthRequestedState();
+
+ void enter();
+ void exit();
+
+ int read(unsigned char *buf, size_t buf_len, size_t *read_len);
+ int write(unsigned char *buf, size_t len);
+ void received_cb(size_t bytes);
+
+ void written_cb(size_t bytes);
+
+ void handle_failure(std::string msg);
+ std::string str() { return "AuthRequested State"; };
+
+private:
+ void read_data(size_t bytes);
+ void recv_auth_response(unsigned char ver, unsigned char auth_type, unsigned char op,
+ int data_len, unsigned char *data);
+ void send_auth_confirm(int accept);
+
+ static void __requested_timeout(void *user_data);
+ vine::VineTimer __timer;
+
+ int __sent_frame;
+ int __accept;
+};
+
+// Vine Datapath is set as VineAuthRespondedState when AUTH_RESPONSE message is sent.
+// It is only for Authenticate Responder, which is usually the client (subscriber).
+// On receiving a AUTH_CONFIRM, it is changed to VineAuthCompletedState.
+// On some failures, it is changed to VineAuthFailedState.
+class VineAuthRespondedState : public VineDataPathState
+{
+public:
+ VineAuthRespondedState(vine_data_path_h dp, vine_dp_plugin_h plugin);
+ ~VineAuthRespondedState();
+
+ void enter();
+ void exit();
+
+ int read(unsigned char *buf, size_t buf_len, size_t *read_len);
+ int write(unsigned char *buf, size_t len);
+ void received_cb(size_t bytes);
+
+ void written_cb(size_t bytes);
+
+ std::string str() { return "AuthResponded State"; };
+
+private:
+ void handle_failure(std::string msg);
+ void read_data(size_t bytes);
+ void recv_auth_confirm(unsigned char ver, unsigned char auth_type, unsigned char op,
+ int data_len, unsigned char *data);
+
+ static void __responded_timeout(void *user_data);
+ vine::VineTimer __timer;
+};
+
+// Vine Datapath is set as VineAuthFailedState when some failures occur in the authentication process.
+// It leads to terminate the Vine Datapath.
+class VineAuthFailedState : public VineDataPathState
+{
+public:
+ VineAuthFailedState(vine_data_path_h dp, vine_dp_plugin_h plugin);
+ ~VineAuthFailedState();
+
+ void enter();
+ void exit();
+
+ int read(unsigned char *buf, size_t buf_len, size_t *read_len);
+ int write(unsigned char *buf, size_t len);
+ void received_cb(size_t bytes);
+
+ void written_cb(size_t bytes);
+
+ std::string str() { return "AuthFailed State"; };
+};
+
+// Vine Datapath set as VineEstablishedState can be used to communicate with its peer.
+class VineEstablishedState : public VineDataPathState
+{
+public:
+ VineEstablishedState(vine_data_path_h dp, vine_dp_plugin_h plugin);
+ ~VineEstablishedState();
+
+ void enter();
+ void exit();
+
+ int read(unsigned char *buf, size_t buf_len, size_t *read_len);
+ int write(unsigned char *buf, size_t len);
+ void received_cb(size_t bytes);
+
+ void written_cb(size_t bytes);
+
+ std::string str() { return "Established State"; };
+};
+
+typedef void (*state_changed_notifier)(vine_data_path_h dp, VineDataPathState *state);
+typedef void (*established_notifier)(vine_data_path_h dp, int result);
+typedef void (*data_received_notifier)(vine_data_path_h dp, size_t bytes);
+void vine_dp_state_set_state_changed_notifier(state_changed_notifier notifier);
+void vine_dp_state_set_established_notifier(established_notifier notifier);
+void vine_dp_state_set_data_received_notifier(data_received_notifier notifier);
+
+VineDataPathState *vine_get_default_state(vine_data_path_h dp, vine_dp_plugin_h plugin);
+void start_connected_state(vine_data_path_h dp, vine_dp_plugin_h plugin, VineDataPathState *state);
+void start_accepted_state(vine_data_path_h dp, vine_dp_plugin_h plugin, VineDataPathState *state);
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VINE_DATA_PATH_H__
+#define __VINE_DATA_PATH_H__
+
+#include "vine.h"
+#include "vine-data-path-state.h"
+#include "vine-event-loop.h"
+
+typedef void *vine_data_path_h;
+
+typedef enum {
+ VINE_DATA_PATH_TYPE_SERVER = 0,
+ VINE_DATA_PATH_TYPE_CLIENT,
+ VINE_DATA_PATH_TYPE_UNKNOWN
+} vine_data_path_type_e;
+
+typedef void (*vine_data_path_received_cb)(vine_data_path_h datapath,
+ size_t received_len, void *user_data);
+typedef void (*vine_data_path_opened_cb)(vine_data_path_h datapath,
+ int result, int port, void *user_data);
+typedef void (*vine_data_path_accepted_cb)(vine_data_path_h datapath, void *user_data);
+typedef void (*vine_data_path_connected_cb)(vine_data_path_h datapath, int result, void *user_data);
+typedef void (*vine_data_path_terminated_cb)(vine_data_path_h datapath, void *user_data);
+
+int _vine_data_path_read(vine_data_path_h datapath,
+ unsigned char *buf, size_t buf_len, size_t *read_len);
+int _vine_data_path_write(vine_data_path_h datapath, unsigned char *buf, size_t len);
+int _vine_data_path_set_received_cb(vine_data_path_h datapath,
+ vine_data_path_received_cb callback, void *user_data);
+int _vine_data_path_unset_received_cb(vine_data_path_h datapath);
+int _vine_data_path_close(vine_data_path_h datapath);
+int _vine_data_path_destroy(vine_data_path_h datapath);
+
+const char *_vine_data_path_get_ip(vine_data_path_h datapath);
+int _vine_data_path_get_port(vine_data_path_h datapath);
+
+int vine_data_path_init(void);
+int vine_data_path_deinit(void);
+int vine_data_path_open(vine_address_family_e addr_family, int port,
+ const char *iface_name, int max_conn, vine_security_h security,
+ vine_data_path_opened_cb opened_cb, void *opened_cb_data,
+ vine_data_path_accepted_cb accepted_cb, void *accepted_cb_data,
+ vine_data_path_h *opened_datapath,
+ vine_event_queue_h event_fd);
+int vine_data_path_connect(vine_address_family_e addr_family,
+ const char *ip, int port,
+ const char *iface_name, vine_security_h security,
+ vine_data_path_connected_cb callback, void *user_data,
+ vine_data_path_h *connected_datapath,
+ vine_event_queue_h event_fd);
+int vine_data_path_set_terminated_cb(vine_data_path_h datapath,
+ vine_data_path_terminated_cb callback, void *user_data);
+
+vine_security_h _vine_dp_get_security(vine_data_path_h datapath);
+
+void notify_connected(vine_data_path_h datapath, int result);
+void notify_accepted(vine_data_path_h datapath, int result);
+void notify_data_received(vine_data_path_h datapath, size_t bytes);
+
+#endif /* __VINE_DATA_PATH_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+#ifndef __VINE_DISC_PLUGIN_H__
+#define __VINE_DISC_PLUGIN_H__
+
+#include <map>
+#include <stdbool.h>
+#include <sys/socket.h>
+
+#define DNS_SD_PLUGIN_PATH "libvine-plugin-dns-sd.so"
+
+using namespace std;
+
+typedef enum {
+ VINE_DISC_ERROR_NONE = 0,
+ VINE_DISC_ERROR_INVALID_PARAMETER,
+ VINE_DISC_ERROR_OPERATION_FAILED,
+ VINE_DISC_ERROR_OUT_OF_MEMORY,
+ VINE_DISC_ERROR_NAME_CONFLICT,
+ VINE_DISC_ERROR_SERVICE_NOT_RUNNING,
+} vine_disc_error;
+
+typedef enum {
+ VINE_DISC_SERVICE_AVAILABLE = 0,
+ VINE_DISC_SERVICE_UNAVAILABLE,
+} vine_disc_service_state;
+
+typedef struct {
+ void (*published_cb)(void *plugin_handle,
+ const char *service_name, vine_disc_error error, void *disc_handle);
+ void (*discovered_cb)(void *plugin_handle, bool available,
+ const char *service_type, const char *service_name,
+ const char *host_name, int port, const map<string, string> &attr,
+ const char *iface_name, int more_coming, void *user_data);
+ void (*ip_resolved_cb)(void *plugin_handle, bool add,
+ const char *ip, sa_family_t address_family, void *user_data);
+ void (*fd_added_cb)(int fd, void *user_data);
+ void (*fd_removed_cb)(int fd, void *user_data);
+} vine_disc_plugin_callbacks;
+
+typedef struct {
+ vine_disc_error (*init)(void **plugin_handle, void *disc_handle);
+ void (*deinit)(void *plugin_handle);
+ vine_disc_error (*publish)(void *plugin_handle,
+ const char *service_type, const char *service_name,
+ int port, const map<string, string> &,
+ const char *iface_name);
+ vine_disc_error (*stop_publish)(void *plugin_handle);
+ vine_disc_error (*subscribe)(void *plugin_handle,
+ const char *service_type, const char *iface_name);
+ vine_disc_error (*stop_subscribe)(void *plugin_handle);
+ vine_disc_error (*resolve_ip)(void *plugin_handle,
+ const char *service_type, const char *service_name,
+ const char *host_name, const char *iface_name);
+ vine_disc_error (*cancel_resolve_ip)(void *plugin_handle);
+ vine_disc_error (*process_event)(void *plugin_handle, int fd);
+
+ void (*register_callbacks)(vine_disc_plugin_callbacks callbacks);
+} vine_disc_plugin_fn;
+
+extern "C" void vine_disc_plugin_init(vine_disc_plugin_fn *fn);
+
+#endif /* __VINE_DISC_PLUGIN_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+#ifndef __VINE_DISC_H__
+#define __VINE_DISC_H__
+
+#include <map>
+#include <stdbool.h>
+
+#include "vine.h"
+#include "vine-event-loop.h"
+#include "vine-service.h"
+
+using namespace std;
+
+typedef void *vine_disc_h;
+typedef void (*vine_disc_published_cb)(vine_disc_h disc,
+ const char *service_name, vine_error_e error, void *user_data);
+typedef void (*vine_disc_discovered_cb)(vine_disc_h disc, bool available,
+ const char *service_type, const char *service_name,
+ const char *host_name, int port, const map<string, string> &attr,
+ const char *iface_name, int more_coming, void *user_data);
+typedef void (*vine_disc_ip_resolved_cb)(vine_disc_h disc, vine_service_h service, bool add,
+ const char *ip, vine_address_family_e address_family, void *user_data);
+
+int vine_disc_init();
+void vine_disc_deinit();
+int vine_disc_create(vine_discovery_method_e disc_method, vine_disc_h *disc);
+void vine_disc_destroy(vine_disc_h disc);
+int vine_disc_publish(vine_disc_h disc,
+ vine_service_h service, const char *iface_name,
+ vine_disc_published_cb cb, void *user_data,
+ vine_event_queue_h event_fd);
+int vine_disc_stop_publish(vine_disc_h disc);
+int vine_disc_subscribe(vine_disc_h disc,
+ const char *service_type, const char *iface_name,
+ vine_disc_discovered_cb cb, void *user_data,
+ vine_event_queue_h event_fd);
+int vine_disc_stop_subscribe(vine_disc_h disc);
+int vine_disc_resolve_ip(vine_disc_h disc,
+ vine_service_h service,
+ vine_disc_ip_resolved_cb cb, void *user_data,
+ vine_event_queue_h event_fd);
+int vine_disc_cancel_resolve_ip(vine_disc_h disc, vine_service_h service);
+
+bool vine_disc_is_plugin_loaded(vine_discovery_method_e disc_method);
+
+#endif /* __VINE_DISC_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <set>
+#include <utility>
+
+#include "vine.h"
+#include "vine-data-path.h"
+#include "vine-disc.h"
+#include "vine-queue.h"
+
+namespace vine {
+
+#define VINE_DP_DEFAULT_CONNECTIONS_NUM 30
+#define VINE_DP_MAX_CONNECTIONS_NUM 200
+
+class DataPath
+{
+public:
+ virtual ~DataPath() {}
+
+ virtual int open(vine_dp_opened_cb callback, void *user_data) = 0;
+ virtual void close() = 0;
+
+ virtual int send(unsigned char *buf, size_t len) = 0;
+ virtual int recv(unsigned char *buf, size_t buf_len, size_t *read_len) = 0;
+
+ virtual int set_addr_family(vine_address_family_e addr_family) = 0;
+ virtual int set_remote_ip(vine_address_family_e, const std::string &ip) = 0;
+ virtual int set_port(int port) = 0;
+ virtual int get_port() = 0;
+ virtual int set_topic(std::string topic) = 0;
+ virtual int set_max_connections(int max_conn) = 0;
+ virtual int set_accepted_cb(vine_dp_accepted_cb callback, void *user_data) = 0;
+ virtual int unset_accepted_cb() = 0;
+
+ void *get_eventfd() { return mEventFd; }
+ int set_security(void *security);
+ int set_iface_name(const std::string &iface_name);
+
+ int set_received_cb(vine_dp_received_cb callback, void *user_data);
+ int unset_received_cb();
+ int set_terminated_cb(vine_dp_terminated_cb callback, void *user_data);
+ int unset_terminated_cb();
+
+ void invoke_opened_cb(int result);
+ void invoke_terminated_cb();
+ void invoke_received_cb(size_t received_len);
+
+protected:
+ void *mEventFd;
+ void *mSecurity;
+ std::string mIfaceName;
+
+ vine_dp_opened_cb mOpenedCb;
+ void *mOpenedCbData;
+ vine_dp_received_cb mReceivedCb;
+ void *mReceivedCbData;
+ vine_dp_terminated_cb mTerminatedCb;
+ void *mTerminatedCbData;
+};
+
+class DPServer : DataPath
+{
+public:
+ DPServer(void *event_fd);
+ virtual ~DPServer();
+
+ virtual int open(vine_dp_opened_cb callback, void *user_data);
+ virtual void close();
+
+ virtual int send(unsigned char *buf, size_t len);
+ virtual int recv(unsigned char *buf, size_t buf_len, size_t *read_len);
+
+ virtual int set_addr_family(vine_address_family_e addr_family);
+ virtual int set_remote_ip(vine_address_family_e, const std::string &ip);
+ virtual int set_port(int port);
+ virtual int get_port(){ return mListenPort; };
+ virtual int set_topic(std::string topic);
+ virtual int set_max_connections(int max_conn);
+ virtual int set_accepted_cb(vine_dp_accepted_cb callback, void *user_data);
+ virtual int unset_accepted_cb();
+
+ void invoke_accepted_cb(vine_dp_h dp);
+ vine_address_family_e get_addr_family() { return mAddrFamily; }
+
+private:
+ vine_data_path_h mDataPath;
+ vine_address_family_e mAddrFamily;
+ int mListenPort;
+ int mMaxConnNum;
+ vine_dp_accepted_cb mAcceptedCb;
+ void *mAcceptedCbData;
+};
+
+class DPClient : DataPath
+{
+public:
+ DPClient(void *event_fd);
+ DPClient(void *event_fd, void *datapath);
+ virtual ~DPClient();
+
+ virtual int open(vine_dp_opened_cb callback, void *user_data);
+ virtual void close();
+ virtual int send(unsigned char *buf, size_t len);
+ virtual int recv(unsigned char *buf, size_t buf_len, size_t *read_len);
+
+ virtual int set_addr_family(vine_address_family_e addr_family);
+ virtual int set_remote_ip(vine_address_family_e, const std::string &ip);
+ virtual int set_port(int port);
+ virtual int get_port(){ return mServerPort; };
+ virtual int set_topic(std::string topic);
+ virtual int set_max_connections(int max_conn){ return VINE_ERROR_NONE; };
+ virtual int set_accepted_cb(vine_dp_accepted_cb callback, void *user_data);
+ virtual int unset_accepted_cb();
+
+private:
+ vine_data_path_h mDataPath;
+ vine_address_family_e mAddrFamily;
+ std::string mServerIp;
+ int mServerPort;
+};
+
+typedef std::pair<std::string, int> DPKey;
+typedef std::map<DPKey, vine_data_path_h> DPMap;
+
+enum {
+ VINE_DP_PUBSUB_OPEN_STATE_NONE = 0,
+ VINE_DP_PUBSUB_OPEN_STATE_WAIT,
+ VINE_DP_PUBSUB_OPEN_STATE_DONE,
+};
+
+enum {
+ VINE_DP_PUBSUB_SD_STATE_NONE = 1 << 0,
+ VINE_DP_PUBSUB_SD_STATE_PUBLISH = 1 << 1,
+ VINE_DP_PUBSUB_SD_STATE_SUBSCRIBE = 1 << 2,
+};
+
+#define VINE_DP_PUBSUB_RANK_KEY "rank"
+#define VINE_DP_PUBSUB_RANK_MAX 53123
+#define VINE_DP_PUBSUB_RANK_LEN 6
+#define VINE_DP_PUBSUB_OPEN_TIMEOUT_MS 2000
+
+class DPPubSub : DataPath
+{
+public:
+ DPPubSub(void *event_fd);
+ virtual ~DPPubSub();
+
+ virtual int open(vine_dp_opened_cb callback, void *user_data);
+ virtual void close();
+ virtual int send(unsigned char *buf, size_t len);
+ virtual int recv(unsigned char *buf, size_t buf_len, size_t *read_len);
+
+ virtual int set_addr_family(vine_address_family_e addr_family);
+ virtual int set_remote_ip(vine_address_family_e, const std::string &ip);
+ virtual int set_port(int port);
+ virtual int get_port(){ return mListenPort; };
+
+ virtual int set_topic(std::string topic);
+ virtual int set_max_connections(int max_conn);
+ virtual int set_accepted_cb(vine_dp_accepted_cb callback, void *user_data);
+ virtual int unset_accepted_cb();
+
+ int connect(const char *ip, int port);
+ int close_server_dp();
+ bool is_joined_peer(const char *service_name, const char *ip, int port);
+ int get_joined_peer();
+ void add_joined_peer(const char *ip, int port, vine_data_path_h datapath);
+ void del_joined_peer(vine_data_path_h datapath);
+ void clear_joined_peer();
+
+ void noti_received_peer(vine_data_path_h datapath, size_t bytes);
+
+ int publish_service();
+ int subscribe_service();
+ void set_service_name(const char *service_name) { mServiceName = service_name; }
+ bool check_if_connect(const char *peer_rank,
+ vine_address_family_e ip_type, const char *peer_ip, int peer_port);
+ int get_rank() { return mRank; }
+ vine_address_family_e get_addr_family() { return mAddrFamily; }
+ int get_max_conns() { return mMaxConnNum; }
+
+ int increase_init_disc_num() { return ++mInitDiscNum; }
+ int decrease_init_disc_num() { return --mInitDiscNum; }
+ void set_open_state(int state) { mOpenState = state; }
+ int get_open_state() { return mOpenState; }
+ int get_sd_pubsub_state() { return mSdPubSubState; }
+
+private:
+ std::string mTopic;
+ vine_disc_h mSdSub;
+ vine_disc_h mSdPub;
+ std::string mServiceName;
+
+ vine_address_family_e mAddrFamily;
+ std::set<std::string> mIpList;
+ int mListenPort;
+ int mMaxConnNum;
+
+ vine_data_path_h mServerDataPath;
+ DPMap mDataPathList;
+ VineQueue<std::pair<vine_data_path_h, size_t>> mRecvDataPathList; // datapath, received bytes
+ int mRank;
+
+ int create_rank();
+ int compare_ip_priority(const char *peer_ip);
+
+ int mInitDiscNum;
+ int mOpenState;
+ int mSdPubSubState;
+ static void _open_timer(void *user_data);
+ vine::VineTimer mOpenTimer;
+};
+
+} // namespace vine
+
+int _vine_dp_create(vine_session_h session, vine_dp_type_e type, vine_dp_h *dp);
+int _vine_dp_destroy(vine_dp_h dp);
+int _vine_dp_set_iface_name(vine_dp_h dp, const char *iface_name);
+int _vine_dp_set_addr_family(vine_dp_h dp, vine_address_family_e addr_family);
+int _vine_dp_set_remote_ip(vine_dp_h dp, vine_address_family_e addr_family, const char *ip);
+int _vine_dp_set_port(vine_dp_h dp, int port);
+int _vine_dp_get_port(vine_dp_h dp, int *port);
+int _vine_dp_set_topic(vine_dp_h dp, const char *topic);
+int _vine_dp_set_max_connections(vine_dp_h dp, int max_conn);
+int _vine_dp_set_security(vine_dp_h dp, vine_security_h security);
+int _vine_dp_set_accepted_cb(vine_dp_h dp, vine_dp_accepted_cb callback, void *user_data);
+int _vine_dp_unset_accepted_cb(vine_dp_h dp);
+int _vine_dp_open(vine_dp_h dp, vine_dp_opened_cb callback, void *user_data);
+int _vine_dp_set_terminated_cb(vine_dp_h dp, vine_dp_terminated_cb callback, void *user_data);
+int _vine_dp_unset_terminated_cb(vine_dp_h dp);
+int _vine_dp_close(vine_dp_h dp);
+int _vine_dp_send(vine_dp_h dp, unsigned char *buf, size_t len);
+int _vine_dp_recv(vine_dp_h dp, unsigned char *buf, size_t buf_len, size_t *read_len);
+int _vine_dp_set_received_cb(vine_dp_h dp, vine_dp_received_cb callback, void *user_data);
+int _vine_dp_unset_received_cb(vine_dp_h dp);
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+#ifndef __VINE_EVENT_LOOP_H__
+#define __VINE_EVENT_LOOP_H__
+
+#include <sys/epoll.h>
+#include <pthread.h>
+
+typedef void *vine_event_queue_h;
+
+typedef void (*vine_poll_handler)(int fd, int events, void *user_data);
+
+// vine_event_handler will call user_cb
+typedef void (*vine_event_handler)(void *event, void *user_data);
+
+typedef void (*vine_event_free_handler)(void *event);
+
+enum {
+ VINE_POLLIN = EPOLLIN,
+ VINE_POLLOUT = EPOLLOUT,
+ VINE_POLLHUP = EPOLLHUP,
+};
+
+int vine_event_loop_init();
+void vine_event_loop_deinit();
+int vine_event_loop_start();
+void vine_event_loop_stop();
+
+int vine_event_queue_create(vine_event_queue_h *event_fd);
+void vine_event_queue_destroy(vine_event_queue_h event_fd);
+
+void vine_event_loop_get_eventfd(vine_event_queue_h event_fd, int *eventfd);
+int vine_event_loop_process(vine_event_queue_h event_fd);
+
+int vine_event_loop_add_io_handler(int fd, int events,
+ vine_poll_handler handler, void *user_data);
+int vine_event_loop_mod_io_handler(int fd, int events,
+ vine_poll_handler handler, void *user_data);
+int vine_event_loop_del_io_handler(int fd);
+
+int vine_event_loop_add_event(vine_event_queue_h event_fd, void *event_data,
+ vine_event_handler handler, vine_event_free_handler free_func,
+ void *user_data);
+
+#define MAX_VINE_EPOLL_EVENTS 1024
+
+#endif /* __VINE_EVENT_LOOP_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+#ifndef __VINE_PRIVATE_H__
+#define __VINE_PRIVATE_H__
+
+int _vine_init();
+int _vine_deinit();
+int _vine_get_capabilities(int type, int *capabilities);
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+#pragma once
+
+#include <mutex>
+#include <queue>
+
+#include "vine-log.h"
+
+template <typename T>
+class VineQueue
+{
+public:
+ VineQueue()
+ {
+ VINE_LOGD("New Queue");
+ }
+ virtual ~VineQueue()
+ {
+ VINE_LOGD("Destroy Queue");
+ }
+
+ void push(const T &element)
+ {
+ std::lock_guard<std::mutex> lock_guard(_q_mutex);
+ _queue.push(element);
+ }
+
+ T pop()
+ {
+ std::lock_guard<std::mutex> lock_guard(_q_mutex);
+ if (_queue.empty())
+ return NULL;
+
+ T element = _queue.front();
+ _queue.pop();
+ return element;
+ }
+
+ void erase() // void pop
+ {
+ std::lock_guard<std::mutex> lock_guard(_q_mutex);
+ _queue.pop();
+ }
+
+ T &front()
+ {
+ return _queue.front();
+ }
+
+private:
+ std::queue<T> _queue;
+ std::mutex _q_mutex;
+};
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VINE_SECURITY_H__
+#define __VINE_SECURITY_H__
+
+#include <stdbool.h>
+
+#include "vine.h"
+
+int _vine_security_create(vine_security_h *security);
+int _vine_security_clone(vine_security_h *cloned, vine_security_h origin);
+int _vine_security_destroy(vine_security_h security);
+int _vine_security_set_type(vine_security_h security, vine_security_type_e type);
+int _vine_security_get_type(vine_security_h security, vine_security_type_e *type);
+int _vine_security_set_tls_version(vine_security_h security, vine_security_tls_version_e version);
+int _vine_security_get_tls_version(vine_security_h security, vine_security_tls_version_e *version);
+int _vine_security_set_verification_flags(vine_security_h security, int flags);
+int _vine_security_get_verification_flags(vine_security_h security, int *flags);
+int _vine_security_set_ca_path(vine_security_h security, const char *ca_path);
+int _vine_security_get_ca_path(vine_security_h security, char **ca_path);
+int _vine_security_set_cert_path(vine_security_h security, const char *cert_path);
+int _vine_security_get_cert_path(vine_security_h security, char **cert_path);
+int _vine_security_set_private_key(vine_security_h security, const char *key_path);
+int _vine_security_get_private_key(vine_security_h security, char **key_path);
+int _vine_security_set_psk(vine_security_h security, const char *psk);
+int _vine_security_get_psk(vine_security_h security, char **psk);
+
+#endif /* __VINE_SECURITY_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VINE_SERVICE_H__
+#define __VINE_SERVICE_H__
+
+#include <map>
+#include <string>
+
+#include <stdbool.h>
+
+#include "vine.h"
+
+using namespace std;
+
+int _vine_service_create(vine_service_h *service, bool published);
+int _vine_service_destroy(vine_service_h service);
+int _vine_service_clone(vine_service_h origin, vine_service_h *cloned);
+
+int _vine_service_set_type(
+ vine_service_h service, const char *service_type);
+const char *_vine_service_get_type(vine_service_h service);
+int _vine_service_set_name(vine_service_h service, const char *service_name);
+const char *_vine_service_get_name(vine_service_h service);
+
+int _vine_service_set_host_name(vine_service_h service, const char *host_name);
+const char *_vine_service_get_host_name(vine_service_h service);
+
+int _vine_service_add_attribute(vine_service_h service,
+ const char *key, const char *value);
+int _vine_service_remove_attribute(vine_service_h service, const char *key);
+map<std::string, std::string> _vine_service_get_attributes(vine_service_h service);
+int _vine_service_foreach_attribute(vine_service_h service,
+ vine_service_attribute_cb callback, void *user_data);
+int _vine_service_get_attributes_as_arrays(vine_service_h service,
+ char **keys, char **values, int *length);
+
+int _vine_service_set_iface_name(vine_service_h service, const char *iface_name);
+const char *_vine_service_get_iface_name(vine_service_h service);
+
+int _vine_service_get_disc_handle(vine_service_h service,
+ vine_discovery_method_e disc_method, void **disc_handle);
+int _vine_service_set_ip_resolved_cb(vine_service_h service,
+ vine_session_ip_resolved_cb callback, void *user_data);
+int _vine_service_unset_ip_resolved_cb(vine_service_h service);
+vine_session_ip_resolved_cb _vine_service_ip_resolved_cb(vine_service_h service);
+void * _vine_service_ip_resolved_cb_data(vine_service_h service);
+
+int _vine_service_set_port(vine_service_h service, int port);
+int _vine_service_get_port(vine_service_h service);
+
+bool _is_discovered_service(vine_service_h service);
+int _vine_service_set_state(vine_service_h service, vine_service_state_e state);
+vine_service_state_e _vine_service_get_state(vine_service_h service);
+
+bool _vine_service_check_service_type(const char *service_type);
+
+int _vine_service_set_disc_handle(vine_service_h service, void *disc_handle);
+
+#endif /* __VINE_SERVICE_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VINE_SESSION_H__
+#define __VINE_SESSION_H__
+
+#include "vine.h"
+
+int _vine_session_create(vine_session_h *session);
+int _vine_session_destroy(vine_session_h session);
+
+int _vine_session_set_service_type(
+ vine_session_h session, const char *service_type);
+int _vine_session_set_service_name(
+ vine_session_h session, const char *service_name);
+
+int _vine_session_add_attribute(vine_session_h session,
+ const char *key, const char *value);
+int _vine_session_set_port(vine_session_h session, int port);
+int _vine_session_set_registered_cb(vine_session_h session,
+ vine_session_registered_cb callback, void *user_data);
+int _vine_session_unset_registered_cb(vine_session_h session);
+int _vine_session_set_discovered_cb(vine_session_h session,
+ vine_session_discovered_cb callback, void *user_data);
+int _vine_session_unset_discovered_cb(vine_session_h session);
+
+int _vine_session_set_discovery_method(vine_session_h session, vine_discovery_method_e method);
+int _vine_session_register(vine_session_h session,
+ vine_service_h service, const char *iface_name);
+int _vine_session_unregister(vine_session_h session);
+int _vine_session_start_discovery(vine_session_h session,
+ const char *service_type, const char *iface_name);
+int _vine_session_stop_discovery(vine_session_h session);
+int _vine_session_set_ip_resolved_cb(vine_session_h session,
+ vine_service_h service, vine_session_ip_resolved_cb callback, void *user_data);
+int _vine_session_unset_ip_resolved_cb(vine_session_h session,
+ vine_service_h service);
+
+int _vine_session_get_event_queue(vine_session_h session, vine_event_queue_h *eq);
+int _vine_session_get_event_fd(vine_session_h session, int *fd);
+int _vine_session_process_event(vine_session_h session);
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+#pragma once
+
+#include <sys/signal.h>
+#include <functional>
+
+#include "vine-constants.h"
+
+VINE_NAMESPACE_BEGIN
+
+class VineTimer {
+public:
+ VineTimer();
+ ~VineTimer();
+
+ void start(unsigned int timeout_ms,
+ std::function<void(void *user_data)> callback, void *user_data);
+ void stop();
+ void expired();
+ bool is_running();
+
+private:
+ static void __timer_handler(int fd, int events, void *user_data);
+ std::function<void(void *user_data)> __timer_cb;
+ void *__data;
+
+ int __timer_fd;
+};
+
+VINE_NAMESPACE_END
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+#ifndef __VINE_UTIL_H__
+#define __VINE_UTIL_H__
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vine-log.h"
+
+#ifndef API
+#define API __attribute__ ((visibility("default")))
+#endif
+
+#if defined(TIZEN_OS) && defined(USE_FEATURE)
+#define TIZEN_FEATURE_VINE "http://tizen.org/feature/network.vine"
+#define CHECK_FEATURE_SUPPORTED\
+ do {\
+ bool feature_supported = FALSE;\
+ if (!system_info_get_platform_bool(TIZEN_FEATURE_VINE, \
+ &feature_supported)) {\
+ if (feature_supported == FALSE) {\
+ VINE_LOGE("%s feature is disabled", feature_name);\
+ __VINE_FUNC_EXIT__;\
+ return VINE_ERROR_NOT_SUPPORTED;\
+ } \
+ } else {\
+ VINE_LOGE("Error - Feature getting from System Info");\
+ __VINE_FUNC_EXIT__;\
+ return VINE_ERROR_OPERATION_FAILED;\
+ }\
+ } while (0)
+#else
+#define CHECK_FEATURE_SUPPORTED
+#endif
+
+
+
+#define RET_IF(expr, fmt, arg...) do { \
+ if (expr) { \
+ VINE_LOGE(fmt, ##arg); \
+ return; \
+ } \
+ } while (0)
+
+#define RET_VAL_IF(expr, val, fmt, arg...) do { \
+ if (expr) { \
+ VINE_LOGE(fmt, ##arg); \
+ return (val); \
+ } \
+ } while (0)
+
+void vine_mutex_lock(pthread_mutex_t *mutex, const char *func, int line);
+void vine_mutex_unlock(pthread_mutex_t *mutex, const char *func, int line);
+
+#define VINE_LOCK(mutex) vine_mutex_lock(mutex, __FUNCTION__, __LINE__)
+#define VINE_UNLOCK(mutex) vine_mutex_unlock(mutex, __FUNCTION__, __LINE__)
+
+#define STRDUP(a) (a) ? strdup(a) : NULL
+
+char *strndup(const char *s, size_t n);
+
+#endif /* __VINE_UTIL_H__ */
--- /dev/null
+# Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+INCLUDE_DIRECTORIES(
+ ${VINE_LOGGER_PATH}
+)
+
+FILE(GLOB VINE_LOGGER_SOURCES ${VINE_LOGGER_PATH}/*.c)
+
+ADD_DEFINITIONS("-fvisibility=default")
+ADD_LIBRARY(${VINE_LOGGER} SHARED ${VINE_LOGGER_SOURCES})
+
+SET_TARGET_PROPERTIES(
+ ${VINE_LOGGER}
+ PROPERTIES
+ SOVERSION ${VINE_VERSION_MAJOR}
+ VERSION ${VINE_VERSION}
+)
+
+INSTALL(TARGETS ${VINE_LOGGER} DESTINATION "${LIB_DIR}")
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifdef ENABLE_INSTRUMENTATION_MODE
+#define _GNU_SOURCE
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "vine-log.h"
+
+#ifdef USE_STDERR_LOGGER
+static void __stderr_log_time(int log_level)
+{}
+
+void __stderr_logger(int log_level, const char *log)
+{
+ __stderr_log_time(log_level);
+ switch (log_level) {
+ case VINE_LOG_DEBUG:
+ fprintf(stderr, "DEBUG ");
+ break;
+ case VINE_LOG_INFO:
+ fprintf(stderr, "INFO ");
+ break;
+ case VINE_LOG_ERROR:
+ fprintf(stderr, "ERROR ");
+ break;
+ default:
+ return;
+ }
+
+ fprintf(stderr, "%s\n", log);
+}
+#endif
+
+static vine_logger __log_func =
+#ifdef USE_STDERR_LOGGER
+__stderr_logger;
+#else
+NULL;
+#endif
+
+static int filter = VINE_LOG_DEBUG | VINE_LOG_INFO | VINE_LOG_ERROR;
+
+API void vine_set_log_level(int log_level)
+{
+ filter = log_level;
+}
+
+API void vine_set_logger(vine_logger func)
+{
+ __log_func = func;
+}
+
+#if defined(TIZEN_OS) && defined(USE_DLOG)
+static void __write_tizen_dlog(int log_level, const char *format, va_list args)
+{
+ int dlog_level = 0;
+ char buf[VINE_MAX_LOG_LEN + 1] = {0, };
+ vsnprintf(buf, VINE_MAX_LOG_LEN, format, args);
+ switch (log_level) {
+ case VINE_LOG_DEBUG:
+ dlog_level = DLOG_DEBUG;
+ break;
+ case VINE_LOG_INFO:
+ dlog_level = DLOG_INFO;
+ break;
+ case VINE_LOG_ERROR:
+ dlog_level = DLOG_ERROR;
+ break;
+ }
+ dlog_print(dlog_level, LOG_TAG, "%s", buf);
+}
+#endif // TIZEN_OS && USE_DLOG
+
+static void __write_vine_logger(int log_level, const char *format, va_list args)
+{
+ if (!__log_func)
+ return;
+
+ char buf[VINE_MAX_LOG_LEN + 1] = {0, };
+ vsnprintf(buf, VINE_MAX_LOG_LEN, format, args);
+ __log_func(log_level, buf);
+}
+
+void _vine_log(int log_level, const char *format, ...)
+{
+ if ((log_level & filter) == 0)
+ return;
+
+ va_list args;
+ va_start(args, format);
+
+#if defined(TIZEN_OS) && defined(USE_DLOG)
+ __write_tizen_dlog(log_level, format, args);
+#endif
+ __write_vine_logger(log_level, format, args);
+
+ va_end(args);
+}
+
+#ifdef ENABLE_INSTRUMENTATION_MODE
+void __cyg_profile_func_enter(void *func, void *caller)
+{
+ Dl_info info;
+ if (dladdr(func, &info) && info.dli_sname)
+ _vine_log(VINE_LOG_DEBUG, "Enter(%s)", info.dli_sname);
+}
+
+void __cyg_profile_func_exit(void *func, void *caller)
+{
+ Dl_info info;
+ if (dladdr(func, &info) && info.dli_sname)
+ _vine_log(VINE_LOG_DEBUG, "Exit(%s)", info.dli_sname);
+}
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+#ifndef __VINE_LOG_H__
+#define __VINE_LOG_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef API
+#define API __attribute__ ((visibility("default")))
+#endif
+
+#include <string.h>
+
+typedef enum {
+ VINE_LOG_DEBUG = 1 << 0,
+ VINE_LOG_INFO = 1 << 1,
+ VINE_LOG_ERROR = 1 << 2,
+} vine_log_level_e;
+
+/**
+ * @brief
+ * @param[in] log_level The level of log.
+ * @param[in] log log
+ * @see vine_set_logger()
+ */
+typedef void(*vine_logger)(int log_level, const char *log);
+
+/**
+ * @brief Set log level to be logged.
+ * @remarks Use bitwise OR operation with elements of vine_log_level_e.
+ * @param[in] log_level The log level to be filtered
+ */
+void vine_set_log_level(int log_level);
+
+/**
+ * @brief Set a log function
+ * @param[in] func
+ */
+void vine_set_logger(vine_logger func);
+
+#if defined(TIZEN_OS) && defined(USE_DLOG)
+#include <dlog.h>
+#undef LOG_TAG
+#define LOG_TAG "VINE"
+#endif
+
+#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
+
+#define VINE_MAX_LOG_LEN 1024
+#define VINE_LOGD(format, args...) \
+ _vine_log(VINE_LOG_DEBUG, "%s:%s(%d) > " format, __FILENAME__, __FUNCTION__, __LINE__, ##args)
+#define VINE_LOGI(format, args...) \
+ _vine_log(VINE_LOG_INFO, "%s:%s(%d) > " format, __FILENAME__, __FUNCTION__, __LINE__, ##args)
+#define VINE_LOGE(format, args...) \
+ _vine_log(VINE_LOG_ERROR, "%s:%s(%d) > " format, __FILENAME__, __FUNCTION__, __LINE__, ##args)
+
+#define __VINE_FUNC_ENTER__ VINE_LOGD("Enter")
+#define __VINE_FUNC_EXIT__ VINE_LOGD("Exit")
+
+void _vine_log(int log_level, const char *format, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __VINE_LOG_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "vine-auth.h"
+#include "vine-log.h"
+
+unsigned char *alloc_auth_frame(unsigned char ver,
+ unsigned char auth_type, unsigned char op,
+ int data_len, const unsigned char *data, int *size)
+{
+ *size = 5 + data_len;
+ unsigned char *buf = new unsigned char[*size];
+ if (buf == NULL) {
+ VINE_LOGE("Out of memory");
+ return NULL;
+ }
+
+ buf[0] = ver;
+ buf[1] = auth_type;
+ buf[2] = op;
+ buf[3] = (0xFF00 & data_len) >> 8;
+ buf[4] = (0xFF & data_len);
+ memcpy(&buf[5], data, data_len);
+
+ return buf;
+}
+
+void release_auth_frame(unsigned char *buf)
+{
+ delete [] buf;
+}
+
+bool parse_auth_frame(unsigned char *buf, size_t buf_size,
+ unsigned char *ver, unsigned char *auth_type,
+ unsigned char *op, int *data_len, unsigned char **data)
+{
+ *ver = buf[0];
+ *auth_type = buf[1];
+ *op = buf[2];
+ *data_len = (buf[3] << 8) | (buf[4]);
+ *data = (unsigned char *)malloc(*data_len);
+ if (*data == NULL) {
+ VINE_LOGE("Out of memory");
+ return false;
+ }
+ memcpy(*data, &buf[5], *data_len);
+
+ // TODO: Need to check if it is an auth frame or not
+ return true;
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")
+{
+}
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vine-auth.h"
+#include "vine-data-path-state.h"
+#include "vine-security.h"
+#include "vine-log.h"
+#include "vine-utils.h"
+
+extern vine_dp_plugin_fn g_dp_plugin_fn;
+
+static state_changed_notifier __set_state;
+static established_notifier __established;
+static data_received_notifier __data_received;
+
+static void state_transition(vine_data_path_h dp,
+ VineDataPathState *old_state, VineDataPathState *new_state)
+{
+ old_state->exit();
+ __set_state(dp, new_state);
+ delete old_state;
+ new_state->enter();
+}
+
+VineDataPathState::VineDataPathState(vine_data_path_h dp, vine_dp_plugin_h plugin)
+ : __dp(dp), __plugin(plugin)
+{}
+
+VineDataPathState::~VineDataPathState()
+{}
+
+VineDefaultState::VineDefaultState(vine_data_path_h dp, vine_dp_plugin_h plugin)
+ : VineDataPathState(dp, plugin)
+{
+}
+
+VineDefaultState::~VineDefaultState()
+{
+}
+
+void VineDefaultState::enter()
+{
+ VINE_LOGD("+");
+}
+
+void VineDefaultState::exit()
+{
+}
+
+int VineDefaultState::read(unsigned char *buf, size_t buf_len, size_t *read_len)
+{
+ return VINE_ERROR_INVALID_OPERATION;
+}
+
+int VineDefaultState::write(unsigned char *buf, size_t len)
+{
+ return VINE_ERROR_INVALID_OPERATION;
+}
+
+void VineDefaultState::received_cb(size_t bytes)
+{}
+
+void VineDefaultState::written_cb(size_t bytes)
+{}
+
+VineAcceptedState::VineAcceptedState(vine_data_path_h dp, vine_dp_plugin_h plugin)
+ : VineDataPathState(dp, plugin), __sent_frame(0), __accept(0)
+{
+}
+
+VineAcceptedState::~VineAcceptedState()
+{
+}
+
+void VineAcceptedState::enter()
+{
+ VINE_LOGD("+");
+
+ vine_security_h security = _vine_dp_get_security(__dp);
+ vine_security_type_e security_type;
+ if (security == NULL) {
+ security_type = VINE_SECURITY_TYPE_NONE;
+ } else {
+ int ret = _vine_security_get_type(security, &security_type);
+ RET_IF(ret, "Fail to get security type");
+ }
+
+ VINE_LOGD("security_type[%d]", security_type);
+ switch (security_type) {
+ case VINE_SECURITY_TYPE_NONE:
+ case VINE_SECURITY_TYPE_TLS:
+ send_auth_confirm(1);
+ __sent_frame = AUTH_OP_CONFIRM;
+ __accept = 1;
+ break;
+ case VINE_SECURITY_TYPE_PSK_OVER_TLS:
+ send_auth_request();
+ __sent_frame = AUTH_OP_REQUEST;
+ break;
+ default:
+ handle_failure("Invaild security type");
+ // TODO: Need to send AUTH_CONFIRM with accept = 0?
+ }
+}
+
+void VineAcceptedState::exit()
+{
+}
+
+int VineAcceptedState::read(unsigned char *buf, size_t buf_len, size_t *read_len)
+{
+ return VINE_ERROR_INVALID_OPERATION;
+}
+
+int VineAcceptedState::write(unsigned char *buf, size_t len)
+{
+ return VINE_ERROR_INVALID_OPERATION;
+}
+
+void VineAcceptedState::received_cb(size_t bytes)
+{
+ handle_failure("Not Established yet");
+}
+
+void VineAcceptedState::written_cb(size_t bytes)
+{
+ VINE_LOGD("+");
+ switch (__sent_frame) {
+ case AUTH_OP_REQUEST:
+ state_transition(__dp, this, new VineAuthRequestedState(__dp, __plugin));
+ break;
+ case AUTH_OP_CONFIRM:
+ if (__accept == 1)
+ state_transition(__dp, this, new VineEstablishedState(__dp, __plugin));
+ else
+ state_transition(__dp, this, new VineAuthFailedState(__dp, __plugin));
+ break;
+ default:
+ VINE_LOGE("Invalid Frame %d", __sent_frame);
+ }
+}
+
+void VineAcceptedState::handle_failure(std::string msg)
+{
+ VINE_LOGE("Failure %s", msg.c_str());
+ state_transition(__dp, this, new VineAuthFailedState(__dp, __plugin));
+}
+
+void VineAcceptedState::send_auth_request()
+{
+ VINE_LOGI("Send AUTH_REQUEST dp[%p]", __dp);
+
+ int size;
+ unsigned char *frame = alloc_auth_frame(AUTH_VERSION,
+ AUTH_TYPE_PSK, AUTH_OP_REQUEST,
+ 0, NULL, &size);
+
+ if (frame == NULL) {
+ handle_failure("Fail to allocate AUTH_REQUEST");
+ return;
+ }
+
+ g_dp_plugin_fn.write(__plugin, frame, size);
+ release_auth_frame(frame);
+}
+
+void VineAcceptedState::send_auth_confirm(int accept)
+{
+ VINE_LOGI("Send AUTH_CONFIRM dp[%p]", __dp);
+
+ int size;
+ unsigned char data[1];
+ data[0] = (unsigned char)accept;
+ unsigned char *frame = alloc_auth_frame(AUTH_VERSION,
+ AUTH_TYPE_OPEN, AUTH_OP_CONFIRM,
+ 1, data, &size);
+
+ if (frame == NULL) {
+ handle_failure("Fail to allocate AUTH_CONFIRM");
+ return;
+ }
+
+ g_dp_plugin_fn.write(__plugin, frame, size);
+ release_auth_frame(frame);
+}
+
+VineConnectedState::VineConnectedState(vine_data_path_h dp, vine_dp_plugin_h plugin)
+ : VineDataPathState(dp, plugin), __sent_frame(0)
+{
+}
+
+VineConnectedState::~VineConnectedState()
+{
+}
+
+void VineConnectedState::__connected_timer(void *user_data)
+{
+ VINE_LOGD("ConnectedState is timed out");
+ VineAuthRequestedState *state = (VineAuthRequestedState *)user_data;
+ state->handle_failure("ConnectedState is timed out");
+}
+
+void VineConnectedState::enter()
+{
+ VINE_LOGD("+");
+ // Waiting for AUTH_REQUEST or AUTH_CONFIRM
+ __timer.start(AUTH_CONNECTED_TIMEOUT_MS, __connected_timer, this);
+}
+
+void VineConnectedState::exit()
+{
+}
+
+int VineConnectedState::read(unsigned char *buf, size_t buf_len, size_t *read_len)
+{
+ return VINE_ERROR_INVALID_OPERATION;
+}
+
+int VineConnectedState::write(unsigned char *buf, size_t len)
+{
+ return VINE_ERROR_INVALID_OPERATION;
+}
+
+void VineConnectedState::received_cb(size_t bytes)
+{
+ VINE_LOGD("+");
+
+ read_data(bytes);
+}
+
+void VineConnectedState::written_cb(size_t bytes)
+{
+ VINE_LOGD("+");
+ switch (__sent_frame) {
+ case AUTH_OP_RESPONSE:
+ state_transition(__dp, this, new VineAuthRespondedState(__dp, __plugin));
+ break;
+ default:
+ VINE_LOGE("Invalid Frame %d", __sent_frame);
+ }
+}
+
+void VineConnectedState::handle_failure(std::string msg)
+{
+ VINE_LOGE("Failure %s", msg.c_str());
+ state_transition(__dp, this, new VineAuthFailedState(__dp, __plugin));
+}
+
+void VineConnectedState::read_data(size_t bytes)
+{
+ unsigned char *buf = (unsigned char *)malloc(bytes);
+ size_t read_size = g_dp_plugin_fn.read(__plugin, buf, bytes);
+ if (bytes != read_size) {
+ // TODO: Check it
+ handle_failure("Wrong size");
+ return;
+ }
+
+ unsigned char ver;
+ unsigned char auth_type;
+ unsigned char op;
+ int data_len;
+ unsigned char *data;
+
+ if (!parse_auth_frame(buf, bytes, &ver, &auth_type, &op, &data_len, &data)) {
+ // TODO: Do we need to handle it in other way?
+ handle_failure("Invaid frame is received");
+ free(buf);
+ return;
+ }
+ free(buf);
+
+ VINE_LOGD("Ver[%x] Auth[%x] Op[%x] DataLen[%d]", ver, auth_type, op, data_len);
+ // TODO: version check
+
+ switch (op) {
+ case AUTH_OP_REQUEST:
+ recv_auth_request(ver, auth_type, op, data_len, data);
+ break;
+ case AUTH_OP_CONFIRM:
+ recv_auth_confirm(ver, auth_type, op, data_len, data);
+ break;
+ default:
+ handle_failure("Invalid frame");
+ }
+ free(data);
+}
+
+void VineConnectedState::recv_auth_request(unsigned char ver,
+ unsigned char auth_type, unsigned char op,
+ int data_len, unsigned char *data)
+{
+ VINE_LOGI("Recv AUTH_REQUEST dp[%p]", __dp);
+ if (auth_type != AUTH_TYPE_PSK) {
+ handle_failure("AUTH_OP_REQUEST should be transmitted with AUTH_TYPE_PSK");
+ return;
+ }
+
+ vine_security_h security = _vine_dp_get_security(__dp);
+ if (security == NULL) {
+ handle_failure("security is NULL (OPEN)");
+ return;
+ }
+ vine_security_type_e security_type;
+ int ret = _vine_security_get_type(security, &security_type);
+ RET_IF(ret, "Fail to get security type");
+
+ if (security_type != VINE_SECURITY_TYPE_PSK_OVER_TLS) {
+ handle_failure("No PSK");
+ return;
+ }
+
+ send_auth_response();
+}
+
+void VineConnectedState::recv_auth_confirm(unsigned char ver,
+ unsigned char auth_type, unsigned char op,
+ int data_len, unsigned char *data)
+{
+ VINE_LOGI("Recv AUTH_CONFIRM dp[%p]", __dp);
+ // If AUTH_CONFIRM (accept = 1) is received,
+ // always accept the message and finish the authentication
+ if (data_len != 1) {
+ handle_failure("No Data");
+ return;
+ }
+
+ if (data[0] != 1) {
+ handle_failure("Not Accepted");
+ return;
+ }
+
+ state_transition(__dp, this, new VineEstablishedState(__dp, __plugin));
+}
+
+void VineConnectedState::send_auth_response()
+{
+ VINE_LOGI("Send AUTH_RESPONSE dp[%p]", __dp);
+
+ vine_security_h security = _vine_dp_get_security(__dp);
+ char *psk;
+ if (_vine_security_get_psk(security, &psk) != VINE_ERROR_NONE) {
+ handle_failure("Fail to get PSK");
+ return;
+ }
+ int size;
+ unsigned char *frame = alloc_auth_frame(AUTH_VERSION,
+ AUTH_TYPE_PSK, AUTH_OP_RESPONSE,
+ strlen(psk), (const unsigned char *)psk, &size);
+
+ if (frame == NULL) {
+ handle_failure("Fail to allocate AUTH_RESPONSE");
+ return;
+ }
+
+ g_dp_plugin_fn.write(__plugin, frame, size);
+ release_auth_frame(frame);
+
+ __sent_frame = AUTH_OP_RESPONSE;
+}
+
+VineAuthRequestedState::VineAuthRequestedState(vine_data_path_h dp, vine_dp_plugin_h plugin)
+ : VineDataPathState(dp, plugin), __sent_frame(0), __accept(0)
+{
+}
+
+VineAuthRequestedState::~VineAuthRequestedState()
+{
+}
+
+void VineAuthRequestedState::__requested_timeout(void *user_data)
+{
+ VINE_LOGD("AUTH_REQUEST is timed out");
+ VineAuthRequestedState *state = (VineAuthRequestedState *)user_data;
+ state->handle_failure("AUTH_REQUEST is timed out");
+}
+
+void VineAuthRequestedState::enter()
+{
+ VINE_LOGD("+");
+ __timer.start(AUTH_REQUEST_TIMEOUT_MS, __requested_timeout, this);
+}
+
+void VineAuthRequestedState::exit()
+{
+}
+
+int VineAuthRequestedState::read(unsigned char *buf, size_t buf_len, size_t *read_len)
+{
+ return VINE_ERROR_INVALID_OPERATION;
+}
+
+int VineAuthRequestedState::write(unsigned char *buf, size_t len)
+{
+ return VINE_ERROR_INVALID_OPERATION;
+}
+
+void VineAuthRequestedState::received_cb(size_t bytes)
+{
+ read_data(bytes);
+}
+
+void VineAuthRequestedState::written_cb(size_t bytes)
+{
+ VINE_LOGD("+");
+ switch (__sent_frame) {
+ case AUTH_OP_CONFIRM:
+ if (__accept == 1)
+ state_transition(__dp, this, new VineEstablishedState(__dp, __plugin));
+ else
+ handle_failure("Not Accepted");
+ break;
+ default:
+ VINE_LOGE("Invalid Frame %d", __sent_frame);
+ }
+}
+
+void VineAuthRequestedState::handle_failure(std::string msg)
+{
+ VINE_LOGE("Failure %s", msg.c_str());
+ state_transition(__dp, this, new VineAuthFailedState(__dp, __plugin));
+}
+
+void VineAuthRequestedState::read_data(size_t bytes)
+{
+ unsigned char *buf = (unsigned char *)malloc(bytes);
+ size_t read_size = g_dp_plugin_fn.read(__plugin, buf, bytes);
+
+ if (bytes != read_size) {
+ // TODO: Check it
+ handle_failure("Wrong size");
+ return;
+ }
+
+ unsigned char ver;
+ unsigned char auth_type;
+ unsigned char op;
+ int data_len;
+ unsigned char *data;
+
+ if (!parse_auth_frame(buf, bytes, &ver, &auth_type, &op, &data_len, &data)) {
+ handle_failure("Invaid frame is received");
+ // TODO: Do we need to handle it in other way?
+ free(buf);
+ return;
+ }
+
+ free(buf);
+
+ VINE_LOGD("Ver[%x] Auth[%x] Op[%x] DataLen[%d]", ver, auth_type, op, data_len);
+ // TODO: version check
+
+ if (op != AUTH_OP_RESPONSE)
+ handle_failure("Invalid op type");
+ else
+ recv_auth_response(ver, auth_type, op, data_len, data);
+
+ free(data);
+}
+
+void VineAuthRequestedState::recv_auth_response(unsigned char ver,
+ unsigned char auth_type, unsigned char op,
+ int data_len, unsigned char *data)
+{
+ VINE_LOGI("Recv AUTH_RESPONSE dp[%p]", __dp);
+ if (auth_type != AUTH_TYPE_PSK) {
+ handle_failure("AUTH_OP_RESPONSE should be transmitted with AUTH_TYPE_PSK");
+ return;
+ }
+
+ vine_security_h security = _vine_dp_get_security(__dp);
+ if (security == NULL) {
+ handle_failure("security is NULL (OPEN)");
+ return;
+ }
+
+ vine_security_type_e security_type;
+ int ret = _vine_security_get_type(security, &security_type);
+ RET_IF(ret, "Fail to get security type");
+
+ if (security_type != VINE_SECURITY_TYPE_PSK_OVER_TLS) {
+ handle_failure("No VINE_SECURITY_TYPE_PSK_OVER_TLS");
+ return;
+ }
+
+ char *psk;
+ if (_vine_security_get_psk(security, &psk) != VINE_ERROR_NONE) {
+ handle_failure("Fail to get PSK");
+ return;
+ }
+
+ __timer.stop();
+ if ((int)strlen(psk) == data_len && memcmp(psk, data, data_len) == 0) {
+ VINE_LOGI("Authentication Confirmed");
+ send_auth_confirm(1);
+ } else {
+ VINE_LOGE("PSK is not matched");
+ send_auth_confirm(0);
+ }
+ free(psk);
+}
+
+void VineAuthRequestedState::send_auth_confirm(int accept)
+{
+ VINE_LOGI("Send AUTH_CONFIRM dp[%p]", __dp);
+
+ int size;
+ unsigned char data[1];
+ data[0] = (unsigned char)accept;
+ unsigned char *frame = alloc_auth_frame(AUTH_VERSION,
+ AUTH_TYPE_OPEN, AUTH_OP_CONFIRM,
+ 1, data, &size);
+
+ if (frame == NULL) {
+ handle_failure("Fail to allocate AUTH_CONFIRM");
+ return;
+ }
+
+ g_dp_plugin_fn.write(__plugin, frame, size);
+ release_auth_frame(frame);
+
+ __sent_frame = AUTH_OP_CONFIRM;
+ __accept = accept;
+}
+
+VineAuthRespondedState::VineAuthRespondedState(vine_data_path_h dp, vine_dp_plugin_h plugin)
+ : VineDataPathState(dp, plugin)
+{
+}
+
+VineAuthRespondedState::~VineAuthRespondedState()
+{
+}
+
+void VineAuthRespondedState::__responded_timeout(void *user_data)
+{
+ VINE_LOGD("AUTH_RESPONSE is timed out");
+ VineAuthRespondedState *state = (VineAuthRespondedState *)user_data;
+ state->handle_failure("AUTH_RESPONSE is timed out");
+}
+
+void VineAuthRespondedState::enter()
+{
+ VINE_LOGD("+");
+ __timer.start(AUTH_RESPONSE_TIMEOUT_MS, __responded_timeout, this);
+}
+
+void VineAuthRespondedState::exit()
+{
+}
+
+int VineAuthRespondedState::read(unsigned char *buf, size_t buf_len, size_t *read_len)
+{
+ return VINE_ERROR_INVALID_OPERATION;
+}
+
+int VineAuthRespondedState::write(unsigned char *buf, size_t len)
+{
+ return VINE_ERROR_INVALID_OPERATION;
+}
+
+void VineAuthRespondedState::received_cb(size_t bytes)
+{
+ read_data(bytes);
+}
+
+void VineAuthRespondedState::written_cb(size_t bytes)
+{
+ VINE_LOGD("+");
+}
+
+void VineAuthRespondedState::handle_failure(std::string msg)
+{
+ VINE_LOGE("Failure %s", msg.c_str());
+ state_transition(__dp, this, new VineAuthFailedState(__dp, __plugin));
+}
+
+void VineAuthRespondedState::read_data(size_t bytes)
+{
+ unsigned char *buf = (unsigned char *)malloc(bytes);
+ size_t read_size = g_dp_plugin_fn.read(__plugin, buf, bytes);
+
+ if (bytes != read_size) {
+ // TODO: Check it
+ handle_failure("Wrong size");
+ return;
+ }
+
+ unsigned char ver;
+ unsigned char auth_type;
+ unsigned char op;
+ int data_len;
+ unsigned char *data;
+
+ if (!parse_auth_frame(buf, bytes, &ver, &auth_type, &op, &data_len, &data)) {
+ handle_failure("Invaid frame is received");
+ // TODO: Do we need to handle it in other way?
+ free(buf);
+ return;
+ }
+
+ free(buf);
+
+ VINE_LOGD("Ver[%x] Auth[%x] Op[%x] DataLen[%d]", ver, auth_type, op, data_len);
+ // TODO: version check
+
+ if (op == AUTH_OP_CONFIRM)
+ recv_auth_confirm(ver, auth_type, op, data_len, data);
+ else
+ handle_failure("Invalid op type");
+
+ free(data);
+}
+
+void VineAuthRespondedState::recv_auth_confirm(unsigned char ver,
+ unsigned char auth_type, unsigned char op,
+ int data_len, unsigned char *data)
+{
+ VINE_LOGI("Recv AUTH_CONFIRM dp[%p]", __dp);
+
+ if (data_len != 1) {
+ handle_failure("No Data");
+ return;
+ }
+
+ if (data[0] != 1) {
+ handle_failure("Not Accepted");
+ return;
+ }
+
+ __timer.stop();
+ state_transition(__dp, this, new VineEstablishedState(__dp, __plugin));
+}
+
+VineAuthFailedState::VineAuthFailedState(vine_data_path_h dp, vine_dp_plugin_h plugin)
+ : VineDataPathState(dp, plugin)
+{
+}
+
+VineAuthFailedState::~VineAuthFailedState()
+{
+}
+
+void VineAuthFailedState::enter()
+{
+ VINE_LOGD("+");
+ __established(__dp, -1);
+}
+
+void VineAuthFailedState::exit()
+{
+}
+
+int VineAuthFailedState::read(unsigned char *buf, size_t buf_len, size_t *read_len)
+{
+ return VINE_ERROR_INVALID_OPERATION;
+}
+
+int VineAuthFailedState::write(unsigned char *buf, size_t len)
+{
+ return VINE_ERROR_INVALID_OPERATION;
+}
+
+void VineAuthFailedState::received_cb(size_t bytes)
+{
+}
+
+void VineAuthFailedState::written_cb(size_t bytes)
+{
+ VINE_LOGD("+");
+}
+
+VineEstablishedState::VineEstablishedState(vine_data_path_h dp, vine_dp_plugin_h plugin)
+ : VineDataPathState(dp, plugin)
+{
+}
+
+VineEstablishedState::~VineEstablishedState()
+{
+}
+
+void VineEstablishedState::enter()
+{
+ VINE_LOGD("+");
+ __established(__dp, 0);
+}
+
+void VineEstablishedState::exit()
+{
+}
+
+int VineEstablishedState::read(unsigned char *buf, size_t buf_len, size_t *read_len)
+{
+ size_t bytes = g_dp_plugin_fn.read(__plugin, buf, buf_len);
+ *read_len = bytes;
+ return VINE_ERROR_NONE;
+}
+
+int VineEstablishedState::write(unsigned char *buf, size_t len)
+{
+ g_dp_plugin_fn.write(__plugin, buf, len);
+ return VINE_ERROR_NONE;
+}
+
+void VineEstablishedState::received_cb(size_t bytes)
+{
+ VINE_LOGD("+");
+ __data_received(__dp, bytes);
+}
+
+void VineEstablishedState::written_cb(size_t bytes)
+{
+ VINE_LOGD("+");
+}
+
+void vine_dp_state_set_state_changed_notifier(state_changed_notifier notifier)
+{
+ VINE_LOGD("vine_dp_state_set_state_changed_notifier");
+ __set_state = notifier;
+}
+
+void vine_dp_state_set_established_notifier(established_notifier notifier)
+{
+ VINE_LOGD("vine_dp_state_set_state_changed_notifier");
+ __established = notifier;
+}
+
+void vine_dp_state_set_data_received_notifier(data_received_notifier notifier)
+{
+ VINE_LOGD("vine_dp_state_set_data_received_notifier");
+ __data_received = notifier;
+}
+
+VineDataPathState *vine_get_default_state(vine_data_path_h dp, vine_dp_plugin_h plugin)
+{
+ return new VineDefaultState(dp, plugin);
+}
+
+void start_connected_state(vine_data_path_h dp, vine_dp_plugin_h plugin,
+ VineDataPathState *state)
+{
+ VINE_LOGD("Connected");
+ state_transition(dp, state, new VineConnectedState(dp, plugin));
+}
+
+void start_accepted_state(vine_data_path_h dp, vine_dp_plugin_h plugin,
+ VineDataPathState *state)
+{
+ VINE_LOGD("Accepted");
+ state_transition(dp, state, new VineAcceptedState(dp, plugin));
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dlfcn.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include "vine-event-loop.h"
+#include "vine-data-path.h"
+#include "vine-data-path-plugin.h"
+#include "vine-security.h"
+#include "vine-log.h"
+#include "vine-utils.h"
+
+typedef struct {
+ vine_data_path_type_e type;
+ char *addr;
+ int port;
+ vine_security_h security;
+
+ vine_data_path_opened_cb opened_cb;
+ void *opened_cb_data;
+ vine_data_path_accepted_cb accepted_cb;
+ void *accepted_cb_data;
+ vine_data_path_connected_cb connected_cb;
+ void *connected_cb_data;
+ vine_data_path_received_cb recv_cb;
+ void *recv_cb_data;
+ vine_data_path_terminated_cb terminated_cb;
+ void *terminated_cb_data;
+
+ vine_dp_plugin_h plugin_handle;
+ vine_event_queue_h event_fd;
+
+ VineDataPathState *state;
+ established_notifier established;
+
+ vine_data_path_h listen_dp; // only for client dp in the server side
+} vine_data_path_s;
+
+typedef struct {
+ int result;
+ int port;
+} vine_dp_opened_event;
+
+typedef struct {
+ int result;
+} vine_dp_connected_event;
+
+typedef struct {
+ size_t bytes;
+} vine_dp_received_event;
+
+typedef struct {
+ vine_data_path_s *connected_dp;
+} vine_dp_accepted_event;
+
+vine_dp_plugin_fn g_dp_plugin_fn = {
+ .init = NULL,
+ .deinit = NULL,
+ .register_callbacks = NULL,
+ .process_event = NULL,
+ .create = NULL,
+ .destroy = NULL,
+ .open = NULL,
+ .connect = NULL,
+ .read = NULL,
+ .write = NULL
+};
+
+static vine_dp_plugin_callbacks g_dp_plugin_cbs = {
+ .pollfd_cb = NULL,
+ .opened_cb = NULL,
+ .accepted_cb = NULL,
+ .connected_cb = NULL,
+ .received_cb = NULL,
+ .written_cb = NULL,
+ .terminated_cb = NULL,
+};
+
+static void (*__init_plugin)(vine_dp_plugin_fn *fn);
+static vine_data_path_s *_vine_data_path_create(vine_data_path_type_e type,
+ vine_security_h security,
+ const char *addr, int port, void *plugin_data, vine_event_queue_h event_fd);
+
+static vine_error_e __convert_data_path_error_to_vine_error(vine_data_path_error error)
+{
+ switch (error) {
+ case VINE_DATA_PATH_ERROR_NONE:
+ return VINE_ERROR_NONE;
+ case VINE_DATA_PATH_ERROR_INVALID_PARAMETER:
+ return VINE_ERROR_INVALID_PARAMETER;
+ case VINE_DATA_PATH_ERROR_INVALID_OPERATION:
+ return VINE_ERROR_INVALID_OPERATION;
+ case VINE_DATA_PATH_ERROR_OPERATION_FAILED:
+ return VINE_ERROR_OPERATION_FAILED;
+ case VINE_DATA_PATH_ERROR_OUT_OF_MEMORY:
+ return VINE_ERROR_OUT_OF_MEMORY;
+ default:
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+}
+
+static vine_error_e __convert_address_family(vine_address_family_e addr_family,
+ vine_dp_addr_family_e *dp_addr_family)
+{
+ switch (addr_family) {
+ case VINE_ADDRESS_FAMILY_DEFAULT:
+ *dp_addr_family = VINE_DP_IPV4_IPV6;
+ break;
+ case VINE_ADDRESS_FAMILY_IPV4:
+ *dp_addr_family = VINE_DP_IPV4;
+ break;
+ case VINE_ADDRESS_FAMILY_IPV6:
+ *dp_addr_family = VINE_DP_IPV6;
+ break;
+ default:
+ return VINE_ERROR_INVALID_PARAMETER;
+ }
+
+ return VINE_ERROR_NONE;
+}
+
+void __vine_dp_poll_handler(int fd, int events, void *user_data)
+{
+ g_dp_plugin_fn.process_event(fd, events);
+}
+
+void __vine_dp_op_handler(int fd, int events, void *user_data)
+{
+ uint64_t v;
+ if (read(fd, &v, sizeof(v)) == -1) {
+ VINE_LOGE("Read error(%d)", errno);
+ return;
+ }
+ g_dp_plugin_fn.process_event(fd, events);
+ vine_event_loop_del_io_handler(fd);
+}
+
+static void __pollfd_cb(vine_data_path_pollfd_op_e op, int fd, int events)
+{
+ switch (op) {
+ case VINE_DATA_PATH_POLLFD_ADD:
+ vine_event_loop_add_io_handler(fd, events, __vine_dp_poll_handler, NULL);
+ break;
+ case VINE_DATA_PATH_POLLFD_DEL:
+ vine_event_loop_del_io_handler(fd);
+ break;
+ case VINE_DATA_PATH_POLLFD_MOD:
+ vine_event_loop_mod_io_handler(fd, events, __vine_dp_poll_handler, NULL);
+ break;
+ case VINE_DATA_PATH_POLLFD_OP:
+ vine_event_loop_add_io_handler(fd, events, __vine_dp_op_handler, NULL);
+ break;
+ case VINE_DATA_PATH_POLLFD_LOCK_UNLOCK:
+ // Do not anything.
+ break;
+ default:
+ VINE_LOGE("invalid operation.");
+ break;
+ }
+}
+
+static void __free_accepted_event(void *data)
+{
+ if (!data)
+ return;
+
+ vine_dp_accepted_event *event = (vine_dp_accepted_event *)data;
+ // CHECK: connected_dp should not be freed
+ free(event);
+}
+
+static void __invoke_accepted_user_cb(void *event, void *user_data)
+{
+ RET_IF(event == NULL, "event is NULL");
+
+ vine_dp_accepted_event *accepted_event = (vine_dp_accepted_event *)event;
+ vine_data_path_s *listen_dp = (vine_data_path_s *)user_data;
+
+ VINE_LOGD("user callback is invoked by event queue.");
+
+ if (listen_dp && listen_dp->accepted_cb)
+ listen_dp->accepted_cb(accepted_event->connected_dp, listen_dp->accepted_cb_data);
+ __free_accepted_event(accepted_event);
+}
+
+void notify_accepted(vine_data_path_h datapath, int result)
+{
+ VINE_LOGD("notify_accepted dp[%p] result[%d]", datapath, result);
+
+ RET_IF(datapath == NULL, "dp is NULL");
+ vine_data_path_s *dp = (vine_data_path_s *)datapath;
+
+ if (result < 0) {
+ // TODO: Handle auth failure
+ VINE_LOGE("Failure dp[%p]", datapath);
+ _vine_data_path_destroy(datapath);
+ return;
+ }
+
+ vine_dp_accepted_event *accepted_event =
+ (vine_dp_accepted_event *)calloc(1, sizeof(vine_dp_accepted_event));
+ RET_IF(accepted_event == NULL, "Out of memory");
+ accepted_event->connected_dp = dp;
+
+ VINE_LOGD("Create a accepted_event[%p]", accepted_event);
+
+ vine_data_path_s *listen_dp = (vine_data_path_s *)dp->listen_dp;
+ VINE_LOGD("listen_dp[%p]", listen_dp);
+ if (listen_dp)
+ vine_event_loop_add_event(listen_dp->event_fd, accepted_event,
+ __invoke_accepted_user_cb, __free_accepted_event, listen_dp);
+}
+
+static void __invoke_opened_user_cb(void *event, void *user_data)
+{
+ RET_IF(event == NULL, "event is null");
+
+ vine_dp_opened_event *opened_event = (vine_dp_opened_event *)event;
+ vine_data_path_s *dp = (vine_data_path_s *)user_data;
+
+ VINE_LOGD("user callback is invoked by event queue");
+
+ if (dp && dp->opened_cb)
+ dp->opened_cb(dp, opened_event->result, opened_event->port, dp->opened_cb_data);
+ free(opened_event);
+}
+static void __opened_cb(int result, int port, void *user_data)
+{
+ RET_IF(user_data == NULL, "dp is NULL");
+
+ vine_data_path_s *dp = (vine_data_path_s *)user_data;
+ vine_dp_opened_event *opened_event =
+ (vine_dp_opened_event *)calloc(1, sizeof(vine_dp_opened_event));
+ opened_event->result = result;
+ opened_event->port = port;
+
+ vine_event_loop_add_event(dp->event_fd, opened_event,
+ __invoke_opened_user_cb, free, dp);
+}
+
+static void __accepted_cb(char *addr, int port, void *plugin_data, void *user_data)
+{
+ RET_IF(user_data == NULL, "listen_dp is NULL");
+
+ vine_data_path_s *listen_dp = (vine_data_path_s *)user_data;
+ vine_data_path_s *connected_dp = _vine_data_path_create(VINE_DATA_PATH_TYPE_SERVER,
+ listen_dp->security, addr, port, plugin_data, listen_dp->event_fd);
+ RET_IF(connected_dp == NULL, "Out of memory");
+
+ VINE_LOGD("Accepted dp[%p] addr[%s] port[%d] listen_dp[%p]", connected_dp, addr, port, user_data);
+ connected_dp->listen_dp = listen_dp;
+
+ VINE_LOGD("Start Authentication");
+ connected_dp->established = notify_accepted;
+ start_accepted_state(connected_dp, connected_dp->plugin_handle, connected_dp->state);
+}
+
+static void __invoke_connected_user_cb(void *event, void *user_data)
+{
+ RET_IF(event == NULL, "event is null");
+
+ vine_dp_connected_event *connected_event = (vine_dp_connected_event *)event;
+ vine_data_path_s *dp = (vine_data_path_s *)user_data;
+
+ VINE_LOGD("user callback is invoked by event queue.");
+
+ if (dp && dp->connected_cb)
+ dp->connected_cb(dp, connected_event->result, dp->connected_cb_data);
+ free(connected_event);
+}
+
+void notify_connected(vine_data_path_h datapath, int result)
+{
+ VINE_LOGD("notify_connected dp[%p] result[%d]", datapath, result);
+
+ RET_IF(datapath == NULL, "dp is NULL");
+ vine_dp_connected_event *connected_event =
+ (vine_dp_connected_event *)calloc(1, sizeof(vine_dp_connected_event));
+ RET_IF(connected_event == NULL, "Out of memory");
+
+ connected_event->result = result;
+ vine_data_path_s *dp = (vine_data_path_s *)datapath;
+ vine_event_loop_add_event(dp->event_fd, connected_event,
+ __invoke_connected_user_cb, free, datapath);
+}
+
+static void __connected_cb(int result, void *user_data)
+{
+ VINE_LOGD("Connected dp[%p] result[%d]", user_data, result);
+
+ RET_IF(user_data == NULL, "dp is NULL");
+
+ vine_data_path_s *dp = (vine_data_path_s *)user_data;
+ if (result < 0) {
+ VINE_LOGE("Fail to connect");
+ notify_connected(dp, result);
+ return;
+ }
+
+ VINE_LOGD("Start Authentication");
+ dp->established = notify_connected;
+ start_connected_state(dp, dp->plugin_handle, dp->state);
+}
+
+static void __invoke_received_user_cb(void *event, void *user_data)
+{
+ RET_IF(event == NULL, "Out of memory");
+
+ vine_dp_received_event *received_event = (vine_dp_received_event *)event;
+ vine_data_path_s *dp = (vine_data_path_s *)user_data;
+
+ VINE_LOGD("user callback is invoked by event queue.");
+
+ if (dp && dp->recv_cb) {
+ // user can read the data in recv_cb().
+ dp->recv_cb(dp, received_event->bytes, dp->recv_cb_data);
+ }
+}
+
+void notify_data_received(vine_data_path_h datapath, size_t bytes)
+{
+ vine_dp_received_event *received_event =
+ (vine_dp_received_event *)calloc(1, sizeof(vine_dp_received_event));
+ RET_IF(received_event == NULL, "Out of memory");
+
+ received_event->bytes = bytes;
+
+ VINE_LOGD("Create a received_event[%p] datapath[%p]", received_event, datapath);
+
+ vine_data_path_s *dp = (vine_data_path_s *)datapath;
+ if (dp)
+ vine_event_loop_add_event(dp->event_fd, received_event,
+ __invoke_received_user_cb, free, datapath);
+}
+
+static void __received_cb(size_t bytes, void *user_data)
+{
+ RET_IF(user_data == NULL, "dp is NULL");
+
+ vine_data_path_s *dp = (vine_data_path_s *)user_data;
+ dp->state->received_cb(bytes);
+}
+
+static void __written_cb(int bytes, void *user_data)
+{
+ VINE_LOGD("%d bytes are written through datapath[%p].", bytes, user_data);
+ vine_data_path_s *dp = (vine_data_path_s *)user_data;
+ dp->state->written_cb(bytes);
+}
+
+static void __invoke_terminated_user_cb(void *event, void *user_data)
+{
+ vine_data_path_s *dp = (vine_data_path_s *)user_data;
+
+ VINE_LOGD("user callback is invoked by event queue.");
+
+ if (dp && dp->terminated_cb)
+ dp->terminated_cb(dp, dp->terminated_cb_data);
+}
+
+static void __terminated_cb(void *user_data)
+{
+ vine_data_path_s *dp = (vine_data_path_s *)user_data;
+ if (dp)
+ vine_event_loop_add_event(dp->event_fd, NULL,
+ __invoke_terminated_user_cb, NULL, user_data);
+}
+
+static int _load_data_path_plugins(void)
+{
+ void *handle = dlopen(DATA_PATH_PLUGIN_PATH, RTLD_LAZY | RTLD_NODELETE);
+ if (handle) {
+ __init_plugin = (void (*)(vine_dp_plugin_fn *))dlsym(handle, "vine_data_path_plugin_init");
+ dlclose(handle);
+ VINE_LOGI("Loaded %s", DATA_PATH_PLUGIN_PATH);
+ } else {
+ VINE_LOGE("%s doesn't exist", DATA_PATH_PLUGIN_PATH);
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_data_path_set_received_cb(
+ vine_data_path_h datapath, vine_data_path_received_cb callback, void *user_data)
+{
+ RET_VAL_IF(datapath == NULL, VINE_ERROR_INVALID_PARAMETER, "datapath is NULL");
+ RET_VAL_IF(callback == NULL, VINE_ERROR_INVALID_PARAMETER, "callback is NULL");
+
+ vine_data_path_s *dp = (vine_data_path_s *)datapath;
+ dp->recv_cb = callback;
+ dp->recv_cb_data = user_data;
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_data_path_unset_received_cb(vine_data_path_h datapath)
+{
+ RET_VAL_IF(datapath == NULL, VINE_ERROR_INVALID_PARAMETER, "datapath is NULL");
+
+ vine_data_path_s *dp = (vine_data_path_s *)datapath;
+ dp->recv_cb = NULL;
+ dp->recv_cb_data = NULL;
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_data_path_write(vine_data_path_h datapath, unsigned char *buf, size_t len)
+{
+ RET_VAL_IF(datapath == NULL, VINE_ERROR_INVALID_PARAMETER, "datapath is NULL");
+ RET_VAL_IF(buf == NULL, VINE_ERROR_INVALID_PARAMETER, "buf is NULL");
+ RET_VAL_IF(len == 0, VINE_ERROR_INVALID_PARAMETER, "len cannot be 0");
+
+ vine_data_path_s *dp = (vine_data_path_s *)datapath;
+ return dp->state->write(buf, len);
+}
+
+int _vine_data_path_read(vine_data_path_h datapath,
+ unsigned char *buf, size_t buf_len, size_t *read_len)
+{
+ RET_VAL_IF(datapath == NULL, VINE_ERROR_INVALID_PARAMETER, "datapath is NULL");
+ RET_VAL_IF(buf == NULL, VINE_ERROR_INVALID_PARAMETER, "buf is NULL");
+ RET_VAL_IF(buf_len == 0, VINE_ERROR_INVALID_PARAMETER, "buf_len cannot be 0");
+ RET_VAL_IF(read_len == NULL, VINE_ERROR_INVALID_PARAMETER, "read_len is NULL");
+
+ vine_data_path_s *dp = (vine_data_path_s *)datapath;
+ return dp->state->read(buf, buf_len, read_len);
+}
+
+int _vine_data_path_close(vine_data_path_h datapath)
+{
+ RET_VAL_IF(datapath == NULL, VINE_ERROR_INVALID_PARAMETER, "datapath is NULL");
+
+ vine_data_path_s *dp = (vine_data_path_s *)datapath;
+ g_dp_plugin_fn.close(dp->plugin_handle);
+
+ return VINE_ERROR_NONE;
+}
+
+// This function will be called when publish session is started
+// or request to connect to a service.
+static vine_data_path_s *_vine_data_path_create(vine_data_path_type_e type,
+ vine_security_h security,
+ const char *addr, int port, void *plugin_data, vine_event_queue_h event_fd)
+{
+ RET_VAL_IF(addr == NULL, NULL, "addr is NULL");
+
+ int ret = VINE_ERROR_NONE;
+ vine_data_path_s *dp = NULL;
+
+ dp = (vine_data_path_s *)calloc(1, sizeof(vine_data_path_s));
+ RET_VAL_IF(dp == NULL, NULL, "Out of memory");
+
+ dp->type = type;
+ dp->addr = STRDUP(addr);
+ if (dp->addr == NULL) {
+ VINE_LOGE("Out of memory");
+ free(dp);
+ return NULL;
+ }
+ dp->port = port;
+ dp->recv_cb = NULL;
+ dp->recv_cb_data = NULL;
+ dp->event_fd = event_fd;
+ ret = _vine_security_clone(&(dp->security), security);
+ if (ret != VINE_ERROR_NONE) {
+ VINE_LOGE("Fail to clone security %d", ret);
+ free(dp->addr);
+ free(dp);
+ return NULL;
+ }
+
+ ret = g_dp_plugin_fn.create(&dp->plugin_handle, plugin_data, dp);
+ if (ret != VINE_DATA_PATH_ERROR_NONE) {
+ free(dp->addr);
+ _vine_security_destroy(security);
+ free(dp);
+ return NULL;
+ }
+
+ dp->state = vine_get_default_state(dp, dp->plugin_handle);
+
+ VINE_LOGD("datapath[%p] is created.", dp);
+
+ return dp;
+}
+
+int _vine_data_path_destroy(vine_data_path_h datapath)
+{
+ RET_VAL_IF(datapath == NULL, VINE_ERROR_INVALID_PARAMETER, "datapath is NULL");
+ vine_data_path_s *dp = (vine_data_path_s *)datapath;
+
+ g_dp_plugin_fn.destroy(dp->plugin_handle);
+ dp->plugin_handle = NULL;
+ free(dp->addr);
+ dp->addr = NULL;
+ delete dp->state;
+ free(datapath);
+
+ return VINE_ERROR_NONE;
+}
+
+const char *_vine_data_path_get_ip(vine_data_path_h datapath)
+{
+ RET_VAL_IF(datapath == NULL, NULL, "datapath is NULL");
+ vine_data_path_s *dp = (vine_data_path_s *)datapath;
+ return (const char *)dp->addr;
+}
+
+int _vine_data_path_get_port(vine_data_path_h datapath)
+{
+ RET_VAL_IF(datapath == NULL, VINE_ERROR_INVALID_PARAMETER, "datapath is NULL");
+ vine_data_path_s *dp = (vine_data_path_s *)datapath;
+ return dp->port;
+}
+
+static void __set_state(vine_data_path_h datapath, VineDataPathState *state)
+{
+ RET_IF(datapath == NULL, "datapath is NULL");
+
+ vine_data_path_s *dp = (vine_data_path_s *)datapath;
+ VINE_LOGD("Datapath[%p] state: %s --> %s",
+ datapath, dp->state->str().c_str(), state->str().c_str());
+ dp->state = state;
+}
+
+static void __established(vine_data_path_h datapath, int result)
+{
+ RET_IF(datapath == NULL, "datapath is NULL");
+
+ vine_data_path_s *dp = (vine_data_path_s *)datapath;
+ dp->established(dp, result);
+}
+
+static void __data_received(vine_data_path_h dp, size_t bytes)
+{
+ notify_data_received(dp, bytes);
+}
+
+int vine_data_path_init(void)
+{
+ int ret = _load_data_path_plugins();
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to load plugins");
+ __init_plugin(&g_dp_plugin_fn);
+
+ ret = g_dp_plugin_fn.init();
+ RET_VAL_IF(ret != VINE_DATA_PATH_ERROR_NONE,
+ __convert_data_path_error_to_vine_error((vine_data_path_error)ret),
+ "Filed to init dp-plugin");
+
+ g_dp_plugin_cbs.pollfd_cb = __pollfd_cb;
+ g_dp_plugin_cbs.opened_cb = __opened_cb;
+ g_dp_plugin_cbs.accepted_cb = __accepted_cb;
+ g_dp_plugin_cbs.connected_cb = __connected_cb;
+ g_dp_plugin_cbs.received_cb = __received_cb;
+ g_dp_plugin_cbs.written_cb = __written_cb;
+ g_dp_plugin_cbs.terminated_cb = __terminated_cb;
+ g_dp_plugin_fn.register_callbacks(g_dp_plugin_cbs);
+
+ vine_dp_state_set_state_changed_notifier(__set_state);
+ vine_dp_state_set_established_notifier(__established);
+ vine_dp_state_set_data_received_notifier(__data_received);
+ return VINE_ERROR_NONE;
+}
+
+int vine_data_path_deinit(void)
+{
+ g_dp_plugin_fn.deinit();
+
+ return VINE_ERROR_NONE;
+}
+
+static vine_dp_tls_version_e __convert_tls_version(vine_security_tls_version_e version)
+{
+ switch (version) {
+ case VINE_SECURITY_TLS_VERSION_DEFAULT:
+ return VINE_DP_TLS_VERSION_DEFAULT;
+ case VINE_SECURITY_TLS_VERSION_1_0:
+ return VINE_DP_TLS_VERSION_1_0;
+ case VINE_SECURITY_TLS_VERSION_1_1:
+ return VINE_DP_TLS_VERSION_1_1;
+ case VINE_SECURITY_TLS_VERSION_1_2:
+ return VINE_DP_TLS_VERSION_1_2;
+ case VINE_SECURITY_TLS_VERSION_1_3:
+ return VINE_DP_TLS_VERSION_1_3;
+ default:
+ return VINE_DP_TLS_VERSION_MAX;
+ }
+}
+
+static void _extract_security_info(vine_security_h src, vine_dp_ssl *dest)
+{
+ RET_IF(src == NULL, "src is NULL");
+ RET_IF(dest == NULL, "dest is NULL");
+
+ vine_security_type_e type = VINE_SECURITY_TYPE_NONE;
+ vine_security_tls_version_e version = VINE_SECURITY_TLS_VERSION_DEFAULT;
+ int flags = 0;
+
+ _vine_security_get_type(src, &type);
+ dest->use_ssl = (type != VINE_SECURITY_TYPE_NONE) ? true : false;
+
+ if (!dest->use_ssl)
+ return;
+
+ _vine_security_get_tls_version(src, &version);
+ dest->tls_version = __convert_tls_version(version);
+
+ _vine_security_get_verification_flags(src, &flags);
+ dest->vflags |= (flags & VINE_SECURITY_VERIFICATION_FLAG_ALLOW_SELF_SIGNED) ?
+ VINE_DATA_PATH_V_FLAG_ALLOW_SS_CERTIFICATE : 0;
+ dest->vflags |= (flags & VINE_SECURITY_VERIFICATION_FLAG_SKIP_HOST_NAME_CHECK) ?
+ VINE_DATA_PATH_V_FLAG_SKIP_CN_CHECK : 0;
+
+ _vine_security_get_ca_path(src, &dest->ca_path);
+ _vine_security_get_cert_path(src, &dest->cert_path);
+ _vine_security_get_private_key(src, &dest->key_path);
+}
+
+static void _destroy_security_info(vine_dp_ssl *dest)
+{
+ RET_IF(dest == NULL, "dest is NULL");
+
+ free(dest->ca_path);
+ free(dest->cert_path);
+ free(dest->key_path);
+}
+
+int vine_data_path_open(vine_address_family_e addr_family, int port, const char *iface_name,
+ int max_conn, vine_security_h security,
+ vine_data_path_opened_cb opened_cb, void *opened_cb_data,
+ vine_data_path_accepted_cb accepted_cb, void *accepted_cb_data,
+ vine_data_path_h *opened_datapath,
+ vine_event_queue_h event_fd)
+{
+ vine_dp_addr_family_e dp_addr_family;
+ int ret = __convert_address_family(addr_family, &dp_addr_family);
+ if (ret != VINE_ERROR_NONE) {
+ VINE_LOGE("Cannot convert address family.");
+ return ret;
+ }
+
+ vine_data_path_s *dp =
+ _vine_data_path_create(VINE_DATA_PATH_TYPE_SERVER, security, "", port, NULL, event_fd);
+ RET_VAL_IF(dp == NULL, VINE_ERROR_OUT_OF_MEMORY, "Out of memory");
+
+ vine_dp_ssl ssl = {false, VINE_DP_TLS_VERSION_DEFAULT, 0, NULL, NULL, NULL};
+ _extract_security_info(security, &ssl);
+
+ dp->opened_cb = opened_cb;
+ dp->opened_cb_data = opened_cb_data;
+ dp->accepted_cb = accepted_cb;
+ dp->accepted_cb_data = accepted_cb_data;
+
+ ret = g_dp_plugin_fn.open(dp->plugin_handle, dp_addr_family, port, iface_name, max_conn, ssl);
+ _destroy_security_info(&ssl);
+
+ if (ret != VINE_DATA_PATH_ERROR_NONE) {
+ _vine_data_path_destroy(dp);
+ return __convert_data_path_error_to_vine_error((vine_data_path_error)ret);
+ }
+
+ *opened_datapath = dp;
+
+ return VINE_ERROR_NONE;
+}
+
+int vine_data_path_connect(vine_address_family_e addr_family,
+ const char *ip, int port, const char *iface_name,
+ vine_security_h security,
+ vine_data_path_connected_cb callback, void *user_data,
+ vine_data_path_h *connected_datapath, vine_event_queue_h event_fd)
+{
+ vine_data_path_s *dp =
+ _vine_data_path_create(VINE_DATA_PATH_TYPE_CLIENT, security, ip, port, NULL, event_fd);
+ RET_VAL_IF(dp == NULL, VINE_ERROR_OUT_OF_MEMORY, "Out of memory");
+
+ dp->addr = STRDUP(ip);
+ dp->port = port;
+ dp->connected_cb = callback;
+ dp->connected_cb_data = user_data;
+
+ vine_dp_ssl ssl = {false, VINE_DP_TLS_VERSION_DEFAULT, 0, NULL, NULL, NULL};
+ _extract_security_info(security, &ssl);
+
+ int ret = g_dp_plugin_fn.connect(dp->plugin_handle,
+ addr_family == VINE_ADDRESS_FAMILY_IPV4 ? VINE_DP_IPV4 : VINE_DP_IPV6,
+ ip, port, iface_name, ssl);
+ _destroy_security_info(&ssl);
+
+ if (connected_datapath)
+ *connected_datapath = dp;
+
+ return __convert_data_path_error_to_vine_error((vine_data_path_error)ret);
+}
+
+int vine_data_path_set_terminated_cb(vine_data_path_h datapath,
+ vine_data_path_terminated_cb callback, void *user_data)
+{
+ RET_VAL_IF(datapath == NULL, VINE_ERROR_INVALID_PARAMETER, "datapath is NULL");
+ RET_VAL_IF(callback == NULL, VINE_ERROR_INVALID_PARAMETER, "callback is NULL");
+
+ vine_data_path_s *dp = (vine_data_path_s *)datapath;
+ dp->terminated_cb = callback;
+ dp->terminated_cb_data = user_data;
+
+ return VINE_ERROR_NONE;
+}
+
+vine_security_h _vine_dp_get_security(vine_data_path_h datapath)
+{
+ RET_VAL_IF(datapath == NULL, NULL, "datapath is NULL");
+
+ vine_data_path_s *dp = (vine_data_path_s *)datapath;
+ return dp->security;
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+
+#include <dlfcn.h>
+#include <net/if.h>
+
+#include "vine.h"
+#include "vine-constants.h"
+#include "vine-service.h"
+#include "vine-disc.h"
+#include "vine-disc-plugin.h"
+#include "vine-log.h"
+#include "vine-utils.h"
+
+static struct {
+ const char *name;
+ const char *path;
+} __vine_disc_plugins_info[] = {
+ [VINE_DISCOVERY_METHOD_DNS_SD] = {"DNS-SD", DNS_SD_PLUGIN_PATH},
+ {NULL, NULL},
+};
+
+static struct {
+ vine_disc_plugin_fn fn;
+ vine_disc_plugin_callbacks callbacks;
+ vine_disc_error (*init)(vine_disc_plugin_fn *fn);
+} __vine_disc_plugins[] = {
+ {{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+ {NULL, NULL, NULL, NULL, NULL}, NULL},
+ {{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+ {NULL, NULL, NULL, NULL, NULL}, NULL},
+};
+
+typedef struct {
+ vine_disc_plugin_fn *plugin_fn;
+
+ vine_disc_published_cb published_cb;
+ void *published_cb_data;
+ vine_disc_discovered_cb discovered_cb;
+ void *discovered_cb_data;
+ vine_disc_ip_resolved_cb ip_resolved_cb;
+ void *ip_resolved_cb_data;
+ vine_service_h service; // Used only for vine_disc_resolve_ip
+
+ void *plugin_handle; // Handle to be used in each plugin
+ vine_event_queue_h event_fd;
+} vine_disc_s;
+
+typedef struct {
+ char service_name[VINE_MAX_SERVICE_NAME_LEN + 1];
+ vine_disc_error error;
+} vine_published_event;
+
+typedef struct {
+ bool available;
+ char service_type[VINE_MAX_SERVICE_TYPE_LEN + 1];
+ char service_name[VINE_MAX_SERVICE_NAME_LEN + 1];
+ char host_name[VINE_MAX_HOST_NAME_LEN + 1];
+ int port;
+ map<string, string> attributes;
+ char iface_name[IF_NAMESIZE + 1];
+ int more_coming;
+} vine_discovered_event;
+
+typedef struct {
+ bool add;
+ vine_address_family_e address_family;
+ char ip[VINE_MAX_IP_LEN + 1];
+ vine_service_h service;
+} vine_ip_resolved_event;
+
+vine_error_e __convert_disc_error_to_vine_error(vine_disc_error error)
+{
+ switch (error) {
+ case VINE_DISC_ERROR_NONE:
+ return VINE_ERROR_NONE;
+ case VINE_DISC_ERROR_NAME_CONFLICT: // DNS-SD changed the service name
+ return VINE_ERROR_NAME_CONFLICT;
+ // TODO: To be determined if which vine error will be returned
+ case VINE_DISC_ERROR_SERVICE_NOT_RUNNING:
+ default:
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+}
+
+static void __invoke_published_user_cb(void *event, void *user_data)
+{
+ VINE_LOGD("event[%p] user_data[%p]", event, user_data);
+ vine_published_event *pub_event = (vine_published_event *)event;
+ vine_disc_s *disc_handle = (vine_disc_s *)user_data;
+ RET_IF(disc_handle == NULL, "Disc handle is NULL");
+
+ VINE_LOGD("Call session callback for published event");
+ if (disc_handle->published_cb)
+ disc_handle->published_cb(disc_handle,
+ pub_event->service_name,
+ __convert_disc_error_to_vine_error(pub_event->error),
+ disc_handle->published_cb_data);
+}
+
+static void __invoke_discovered_user_cb(void *event, void *user_data)
+{
+ VINE_LOGD("event[%p] user_data[%p]", event, user_data);
+ vine_discovered_event *discovered_event = (vine_discovered_event *)event;
+ vine_disc_s *disc_handle = (vine_disc_s *)user_data;
+ RET_IF(disc_handle == NULL, "Disc handle is NULL");
+ RET_IF(event == NULL, "Discovered event is NULL");
+
+ VINE_LOGD("Call session callback for discovered event");
+
+ if (disc_handle->discovered_cb)
+ disc_handle->discovered_cb(disc_handle,
+ discovered_event->available,
+ discovered_event->service_type,
+ discovered_event->service_name,
+ discovered_event->host_name,
+ discovered_event->port,
+ discovered_event->attributes,
+ discovered_event->iface_name,
+ discovered_event->more_coming,
+ disc_handle->discovered_cb_data);
+}
+
+static void __invoke_ip_resolved_user_cb(void *event, void *user_data)
+{
+ VINE_LOGD("event[%p] user_data[%p]", event, user_data);
+ vine_ip_resolved_event *resolved_event = (vine_ip_resolved_event *)event;
+ vine_disc_s *disc_handle = (vine_disc_s *)user_data;
+ RET_IF(disc_handle == NULL, "Disc handle is NULL");
+ RET_IF(event == NULL, "Discovered event is NULL");
+
+ VINE_LOGD("Call session callback for ip resolved event");
+
+ if (disc_handle->ip_resolved_cb)
+ disc_handle->ip_resolved_cb(disc_handle,
+ disc_handle->service,
+ resolved_event->add,
+ resolved_event->ip,
+ resolved_event->address_family,
+ disc_handle->ip_resolved_cb_data);
+}
+
+
+static void __free_pub_event(void *data)
+{
+ VINE_LOGD("Free pub_event[%p]", data);
+ free(data);
+}
+
+static void __free_discovered_event(void *data)
+{
+ VINE_LOGD("Free discovered_event[%p]", data);
+ vine_discovered_event *discovered_event = (vine_discovered_event *)data;
+
+ discovered_event->attributes.clear();
+ free(data);
+}
+
+static void __free_ip_resolved_event(void *data)
+{
+ VINE_LOGD("Free ip_resolved_event[%p]", data);
+ free(data);
+}
+
+static void __published_cb(void *plugin_handle,
+ const char *service_name, vine_disc_error error, void *user_data)
+{
+ VINE_LOGD("Published callback from plugin");
+ VINE_LOGD("service_name[%s] error [%d] user_data[%p]",
+ service_name, error, user_data);
+
+ vine_published_event *pub_event =
+ (vine_published_event *)calloc(1, sizeof(vine_published_event));
+ RET_IF(pub_event == NULL, "Out of memory");
+ strncpy(pub_event->service_name, service_name, VINE_MAX_SERVICE_NAME_LEN);
+ pub_event->error = error;
+
+ VINE_LOGD("Create a pub_event[%p]", pub_event);
+
+ vine_disc_s *disc_handle = (vine_disc_s *)user_data;
+ if (disc_handle)
+ vine_event_loop_add_event(disc_handle->event_fd, pub_event,
+ __invoke_published_user_cb, __free_pub_event, user_data);
+}
+
+static void __discovered_cb(void *plugin_handle, bool available,
+ const char *service_type, const char *service_name,
+ const char *host_name, int port, const map<string, string> &attr,
+ const char *iface_name, int more_coming, void *user_data)
+{
+ VINE_LOGD("Discovered callback from plugin available[%d]", available);
+ VINE_LOGD("service type[%s] service_name[%s] host_name[%s] port[%d] iface[%s] user_data[%p]",
+ service_type, service_name, host_name, port, iface_name, user_data);
+
+ vine_discovered_event *discovered_event = new vine_discovered_event;
+
+ discovered_event->available = available;
+ strncpy(discovered_event->service_type, service_type, VINE_MAX_SERVICE_TYPE_LEN);
+ strncpy(discovered_event->service_name, service_name, VINE_MAX_SERVICE_NAME_LEN);
+ strncpy(discovered_event->iface_name, iface_name, IF_NAMESIZE);
+ if (host_name != NULL)
+ strncpy(discovered_event->host_name, host_name, VINE_MAX_HOST_NAME_LEN);
+
+ discovered_event->port = port;
+ discovered_event->attributes = attr;
+ discovered_event->more_coming = more_coming;
+
+ VINE_LOGD("Create a discovered_event[%p]", discovered_event);
+
+ vine_disc_s *disc_handle = (vine_disc_s *)user_data;
+ if (disc_handle)
+ vine_event_loop_add_event(disc_handle->event_fd, discovered_event,
+ __invoke_discovered_user_cb, __free_discovered_event,
+ user_data);
+}
+
+static void __ip_resolved_cb(void *plugin_handle, bool add,
+ const char *ip, sa_family_t address_family, void *user_data)
+{
+ VINE_LOGD("IP resolved callback from plugin Add[%d]", add);
+ VINE_LOGD("IP[%s] address family[%d] user_data[%p]",
+ ip, address_family, user_data);
+
+ vine_ip_resolved_event *resolved_event = new vine_ip_resolved_event;
+
+ resolved_event->add = add;
+ if (address_family == AF_INET)
+ resolved_event->address_family = VINE_ADDRESS_FAMILY_IPV4;
+ else if (address_family == AF_INET6)
+ resolved_event->address_family = VINE_ADDRESS_FAMILY_IPV6;
+ else
+ VINE_LOGE("Invalid address family %d", address_family);
+
+ strncpy(resolved_event->ip, ip, VINE_MAX_IP_LEN);
+
+ VINE_LOGD("Create a IP resolved event[%p]", resolved_event);
+
+ vine_disc_s *disc_handle = (vine_disc_s *)user_data;
+ if (disc_handle)
+ vine_event_loop_add_event(disc_handle->event_fd, resolved_event,
+ __invoke_ip_resolved_user_cb, __free_ip_resolved_event,
+ user_data);
+}
+
+void __vine_disc_epoll_handler(int fd, int events, void *user_data)
+{
+ VINE_LOGD("Process event for fd[%d] events[%d] disc_handle[%p]", fd, events, user_data);
+ vine_disc_s *disc_handle = (vine_disc_s *)user_data;
+ if (disc_handle->plugin_fn->process_event == NULL) {
+ VINE_LOGE("No process_event() defined");
+ return;
+ }
+ vine_disc_error err = disc_handle->plugin_fn->process_event(disc_handle->plugin_handle, fd);
+ if (err != VINE_DISC_ERROR_NONE) {
+ VINE_LOGE("Fail to process event %d", err);
+ vine_event_loop_del_io_handler(fd);
+ }
+}
+
+void __fd_added_cb(int fd, void *user_data)
+{
+ RET_IF(fd < 0, "Invalid fd");
+ vine_disc_h disc = (vine_disc_h)user_data;
+ VINE_LOGD("New fd to be listened");
+ VINE_LOGD("Add epoll handler for fd[%d]. vine_disc_h[%p]", fd, disc);
+
+ vine_event_loop_add_io_handler(fd, VINE_POLLIN | VINE_POLLHUP,
+ __vine_disc_epoll_handler, disc);
+}
+
+void __fd_removed_cb(int fd, void *user_data)
+{
+ VINE_LOGD("fd[%d] will be removed. vine_disc_h[%p]", fd, user_data);
+ RET_IF(fd < 0, "Invalid fd");
+ vine_event_loop_del_io_handler(fd);
+}
+
+static int __load_disc_plugins()
+{
+ for (int i = 0; __vine_disc_plugins_info[i].path; ++i) {
+ void *handle = dlopen(__vine_disc_plugins_info[i].path, RTLD_LAZY | RTLD_NODELETE);
+ if (handle) {
+ __vine_disc_plugins[i].init =
+ (vine_disc_error (*)(vine_disc_plugin_fn *))
+ dlsym(handle, "vine_disc_plugin_init");
+ dlclose(handle);
+ VINE_LOGI("Loaded %s", __vine_disc_plugins_info[i].path);
+ } else {
+ VINE_LOGE("%s doesn't exist", __vine_disc_plugins_info[i].path);
+ if (i == VINE_DISCOVERY_METHOD_DNS_SD) {
+ VINE_LOGE("%s is a default plugins. It should be loaded.",
+ __vine_disc_plugins_info[i].name);
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+ }
+ }
+
+ return VINE_ERROR_NONE;
+}
+
+static void __init_plugins()
+{
+ for (int i = 0; __vine_disc_plugins[i].init; ++i) {
+ __vine_disc_plugins[i].init(&__vine_disc_plugins[i].fn);
+ __vine_disc_plugins[i].callbacks.published_cb = __published_cb;
+ __vine_disc_plugins[i].callbacks.discovered_cb = __discovered_cb;
+ __vine_disc_plugins[i].callbacks.ip_resolved_cb = __ip_resolved_cb;
+ __vine_disc_plugins[i].callbacks.fd_added_cb = __fd_added_cb;
+ __vine_disc_plugins[i].callbacks.fd_removed_cb = __fd_removed_cb;
+ __vine_disc_plugins[i].fn.register_callbacks(__vine_disc_plugins[i].callbacks);
+ }
+}
+
+int vine_disc_init()
+{
+ int ret = __load_disc_plugins();
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to load plugins");
+
+ __init_plugins();
+
+ return VINE_ERROR_NONE;
+}
+
+void vine_disc_deinit()
+{
+}
+
+int vine_disc_create(vine_discovery_method_e disc_method, vine_disc_h *disc)
+{
+ RET_VAL_IF(!vine_disc_is_plugin_loaded(disc_method), VINE_ERROR_OPERATION_FAILED,
+ "Plugin is not loaded");
+
+ vine_disc_s *disc_handle = (vine_disc_s *)calloc(1, sizeof(vine_disc_s));
+ RET_VAL_IF(disc_handle == NULL, VINE_ERROR_OUT_OF_MEMORY, "Out of Memory");
+
+ *disc = disc_handle;
+ disc_handle->plugin_fn = &__vine_disc_plugins[disc_method].fn;
+ VINE_LOGD("New Discovery handle[%p] disc_method[%d]", disc_handle, disc_method);
+
+ return VINE_ERROR_NONE;
+}
+
+void vine_disc_destroy(vine_disc_h disc)
+{
+ free(disc);
+}
+
+static void __vine_disc_set_published_cb(vine_disc_h disc,
+ vine_disc_published_cb cb, void *user_data)
+{
+ vine_disc_s *disc_handle = (vine_disc_s *)disc;
+ disc_handle->published_cb = cb;
+ disc_handle->published_cb_data = user_data;
+}
+
+static void __vine_disc_set_discovered_cb(vine_disc_h disc,
+ vine_disc_discovered_cb cb, void *user_data)
+{
+ vine_disc_s *disc_handle = (vine_disc_s *)disc;
+ disc_handle->discovered_cb = cb;
+ disc_handle->discovered_cb_data = user_data;
+}
+
+static void __vine_disc_set_ip_resolved_cb(vine_disc_h disc,
+ vine_disc_ip_resolved_cb cb, void *user_data)
+{
+ vine_disc_s *disc_handle = (vine_disc_s *)disc;
+ disc_handle->ip_resolved_cb = cb;
+ disc_handle->ip_resolved_cb_data = user_data;
+}
+
+vine_error_e __vine_disc_plugin_publish(vine_disc_h disc, vine_service_h service,
+ const char *iface_name)
+{
+ RET_VAL_IF(disc == NULL, VINE_ERROR_INVALID_OPERATION, "disc is NULL");
+
+ VINE_LOGD("service[%p], disc handle[%p]", service, disc);
+ vine_disc_s *disc_handle = (vine_disc_s *)disc;
+ void *plugin_handle = NULL;
+
+ if (disc_handle->plugin_fn->init == NULL) {
+ VINE_LOGE("No init() defined");
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+
+ vine_disc_error error = disc_handle->plugin_fn->init(&plugin_handle, disc);
+ RET_VAL_IF(error != VINE_DISC_ERROR_NONE,
+ __convert_disc_error_to_vine_error(error),
+ "Fail to init %d", error);
+
+ disc_handle->plugin_handle = plugin_handle;
+ VINE_LOGD("plugin handle[%p]", plugin_handle);
+
+ if (disc_handle->plugin_fn->publish == NULL) {
+ VINE_LOGE("No publish() defined");
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+
+ error = disc_handle->plugin_fn->publish(plugin_handle,
+ _vine_service_get_type(service), _vine_service_get_name(service),
+ _vine_service_get_port(service), _vine_service_get_attributes(service),
+ iface_name);
+ if (error != VINE_DISC_ERROR_NONE) {
+ VINE_LOGE("Fail to publish %d", error);
+ disc_handle->plugin_fn->deinit(plugin_handle);
+ return __convert_disc_error_to_vine_error(error);
+ }
+
+ return VINE_ERROR_NONE;
+}
+
+int vine_disc_publish(vine_disc_h disc,
+ vine_service_h service, const char *iface_name,
+ vine_disc_published_cb cb, void *user_data,
+ vine_event_queue_h event_fd)
+{
+ RET_VAL_IF(disc == NULL, VINE_ERROR_INVALID_PARAMETER, "disc is NULL");
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+
+ VINE_LOGD("Publishevent_fd[%p]", event_fd);
+ VINE_LOGD("service[%p] disc[%p]", service, disc);
+ __vine_disc_set_published_cb(disc, cb, user_data);
+
+ vine_disc_s *disc_handle = (vine_disc_s *)disc;
+ disc_handle->event_fd = event_fd;
+
+ int ret = __vine_disc_plugin_publish(disc, service, iface_name);
+ if (ret != VINE_ERROR_NONE) {
+ VINE_LOGE("Fail to publish the service %d", ret);
+ return ret;
+ }
+
+ return VINE_ERROR_NONE;
+}
+
+vine_error_e __vine_disc_plugin_stop_publish(vine_disc_h disc)
+{
+ RET_VAL_IF(disc == NULL, VINE_ERROR_INVALID_OPERATION, "disc is NULL");
+
+ vine_disc_error error = VINE_DISC_ERROR_NONE;
+ vine_disc_s *disc_handle = (vine_disc_s *)disc;
+
+ if (disc_handle->plugin_fn->init == NULL) {
+ VINE_LOGE("No init() defined");
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+
+ if (disc_handle->plugin_fn->stop_publish == NULL) {
+ VINE_LOGE("No stop_publish() defined");
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+
+ error = disc_handle->plugin_fn->stop_publish(disc_handle->plugin_handle);
+ if (error != VINE_DISC_ERROR_NONE) {
+ VINE_LOGE("Fail to stop publish %d", error);
+ return __convert_disc_error_to_vine_error(error);
+ }
+
+ return VINE_ERROR_NONE;
+}
+
+int vine_disc_stop_publish(vine_disc_h disc)
+{
+ RET_VAL_IF(disc == NULL, VINE_ERROR_INVALID_PARAMETER, "disc is NULL");
+
+ int ret = __vine_disc_plugin_stop_publish(disc);
+ if (ret != VINE_ERROR_NONE) {
+ VINE_LOGE("Fail to stop publish %d", ret);
+ return ret;
+ }
+
+ return VINE_ERROR_NONE;
+}
+
+vine_error_e __vine_disc_plugin_subscribe(vine_disc_h disc,
+ const char *service_type, const char *iface_name)
+{
+ RET_VAL_IF(disc == NULL, VINE_ERROR_INVALID_OPERATION, "disc is NULL");
+
+ VINE_LOGD("service_type[%s], disc handle[%p]", service_type, disc);
+ vine_disc_s *disc_handle = (vine_disc_s *)disc;
+ void *plugin_handle = NULL;
+
+ if (disc_handle->plugin_fn->init == NULL) {
+ VINE_LOGE("No init() defined");
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+ vine_disc_error error = disc_handle->plugin_fn->init(&plugin_handle, disc);
+ RET_VAL_IF(error != VINE_DISC_ERROR_NONE,
+ __convert_disc_error_to_vine_error(error),
+ "Fail to init %d", error);
+
+ disc_handle->plugin_handle = plugin_handle;
+ VINE_LOGD("plugin handle[%p]", plugin_handle);
+
+ if (disc_handle->plugin_fn->subscribe == NULL) {
+ VINE_LOGE("No subscribe() defined");
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+ error = disc_handle->plugin_fn->subscribe(plugin_handle, service_type, iface_name);
+ if (error != VINE_DISC_ERROR_NONE) {
+ VINE_LOGE("Fail to subscribe %d", error);
+ disc_handle->plugin_fn->deinit(plugin_handle);
+ disc_handle->plugin_handle = NULL;
+ return __convert_disc_error_to_vine_error(error);
+ }
+
+ return VINE_ERROR_NONE;
+}
+
+int vine_disc_subscribe(vine_disc_h disc,
+ const char *service_type, const char *iface_name,
+ vine_disc_discovered_cb cb, void *user_data,
+ vine_event_queue_h event_fd)
+{
+ RET_VAL_IF(disc == NULL, VINE_ERROR_INVALID_PARAMETER, "disc is NULL");
+
+ VINE_LOGD("Subscribe event_fd[%p]", event_fd);
+ VINE_LOGD("service_type[%s] disc[%p]", service_type, disc);
+
+ __vine_disc_set_discovered_cb(disc, cb, user_data);
+
+ vine_disc_s *disc_handle = (vine_disc_s *)disc;
+ disc_handle->event_fd = event_fd;
+
+ int ret = __vine_disc_plugin_subscribe(disc, service_type, iface_name);
+ if (ret != VINE_ERROR_NONE) {
+ VINE_LOGE("Fail to subscribe the service %d", ret);
+ return ret;
+ }
+
+ return VINE_ERROR_NONE;
+}
+
+vine_error_e __vine_disc_plugin_stop_subscribe(vine_disc_h disc)
+{
+ RET_VAL_IF(disc == NULL, VINE_ERROR_INVALID_OPERATION, "disc is NULL");
+
+ vine_disc_error error = VINE_DISC_ERROR_NONE;
+ vine_disc_s *disc_handle = (vine_disc_s *)disc;
+
+ if (disc_handle->plugin_fn->init == NULL) {
+ VINE_LOGE("No init() defined");
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+
+ if (disc_handle->plugin_fn->stop_subscribe == NULL) {
+ VINE_LOGE("No stop_subscribe() defined");
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+
+ error = disc_handle->plugin_fn->stop_subscribe(disc_handle->plugin_handle);
+ if (error != VINE_DISC_ERROR_NONE) {
+ VINE_LOGE("Fail to stop publish %d", error);
+ return __convert_disc_error_to_vine_error(error);
+ }
+
+ return VINE_ERROR_NONE;
+}
+
+int vine_disc_stop_subscribe(vine_disc_h disc)
+{
+ RET_VAL_IF(disc == NULL, VINE_ERROR_INVALID_PARAMETER, "disc is NULL");
+
+ int ret = __vine_disc_plugin_stop_subscribe(disc);
+ if (ret != VINE_ERROR_NONE) {
+ VINE_LOGE("Fail to stop subscribe %d", ret);
+ return ret;
+ }
+
+ return VINE_ERROR_NONE;
+}
+
+vine_error_e __vine_disc_plugin_resolve_ip(vine_disc_h disc, vine_service_h service)
+{
+ RET_VAL_IF(disc == NULL, VINE_ERROR_INVALID_OPERATION, "disc is NULL");
+
+ VINE_LOGD("service[%p], disc handle[%p]", service, disc);
+ vine_disc_s *disc_handle = (vine_disc_s *)disc;
+ void *plugin_handle = disc_handle->plugin_handle;
+ if (disc_handle->plugin_fn == NULL || disc_handle->plugin_fn->init == NULL) {
+ VINE_LOGE("No init() defined");
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+
+ VINE_LOGD("plugin handle[%p]", plugin_handle);
+
+ if (disc_handle->plugin_fn->resolve_ip == NULL) {
+ VINE_LOGE("No resolve_ip() defined");
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+ vine_disc_error error = disc_handle->plugin_fn->resolve_ip(plugin_handle,
+ _vine_service_get_type(service), _vine_service_get_name(service),
+ _vine_service_get_host_name(service), _vine_service_get_iface_name(service));
+ if (error != VINE_DISC_ERROR_NONE) {
+ VINE_LOGE("Fail to resolve ip %d", error);
+ disc_handle->plugin_fn->deinit(plugin_handle);
+ disc_handle->plugin_handle = NULL;
+ return __convert_disc_error_to_vine_error(error);
+ }
+
+ return VINE_ERROR_NONE;
+}
+
+int vine_disc_resolve_ip(vine_disc_h disc,
+ vine_service_h service,
+ vine_disc_ip_resolved_cb cb, void *user_data,
+ vine_event_queue_h event_fd)
+{
+ RET_VAL_IF(disc == NULL, VINE_ERROR_INVALID_PARAMETER, "disc is NULL");
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+
+ VINE_LOGD("Resolve IP for a service[%p] event_fd[%p]", service, event_fd);
+
+ __vine_disc_set_ip_resolved_cb(disc, cb, user_data);
+
+ vine_disc_s *disc_handle = (vine_disc_s *)disc;
+ disc_handle->event_fd = event_fd;
+ disc_handle->service = service;
+
+ int ret = __vine_disc_plugin_resolve_ip(disc, service);
+ if (ret != VINE_ERROR_NONE) {
+ VINE_LOGE("Fail to subscribe the service %d", ret);
+ return ret;
+ }
+
+ return VINE_ERROR_NONE;
+}
+
+vine_error_e __vine_disc_plugin_cancel_resolve_ip(vine_disc_h disc, vine_service_h service)
+{
+ RET_VAL_IF(disc == NULL, VINE_ERROR_INVALID_OPERATION, "disc is NULL");
+
+ VINE_LOGD("service[%p], disc handle[%p]", service, disc);
+ vine_disc_s *disc_handle = (vine_disc_s *)disc;
+ void *plugin_handle = disc_handle->plugin_handle;
+ VINE_LOGD("plugin handle[%p]", plugin_handle);
+
+ if (disc_handle->plugin_fn->cancel_resolve_ip == NULL) {
+ VINE_LOGE("No resolve_ip() defined");
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+
+ vine_disc_error error = disc_handle->plugin_fn->cancel_resolve_ip(plugin_handle);
+ if (error != VINE_DISC_ERROR_NONE)
+ VINE_LOGE("Fail to cancel_resolve_ip %d", error);
+
+ return __convert_disc_error_to_vine_error(error);
+}
+
+int vine_disc_cancel_resolve_ip(vine_disc_h disc, vine_service_h service)
+{
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+ RET_VAL_IF(disc == NULL, VINE_ERROR_INVALID_PARAMETER, "disc is NULL");
+
+ VINE_LOGD("Cancel resolving IP for a service[%p] disc_handle[%p]", service, disc);
+
+ int ret = __vine_disc_plugin_cancel_resolve_ip(disc, service);
+ if (ret != VINE_ERROR_NONE) {
+ VINE_LOGE("Fail to cancel resolving IP %d", ret);
+ return ret;
+ }
+
+ return VINE_ERROR_NONE;
+}
+
+bool vine_disc_is_plugin_loaded(vine_discovery_method_e disc_method)
+{
+ return __vine_disc_plugins[(int)disc_method].init != NULL;
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")
+{
+}
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vine-dp.h"
+
+#include "vine-constants.h"
+#include "vine-data-path.h"
+#include "vine-event-loop.h"
+#include "vine-log.h"
+#include "vine-session.h"
+#include "vine-security.h"
+#include "vine-utils.h"
+
+#include <algorithm>
+#include <string>
+#include <utility>
+#include <arpa/inet.h>
+
+using namespace vine;
+
+extern vine_dp_plugin_fn g_dp_plugin_fn;
+
+static bool _check_if_valid_ip(vine_address_family_e addr_family, const char *ip)
+{
+ int ret = 0;
+ unsigned char buf[sizeof(struct in6_addr)];
+
+ if (addr_family == VINE_ADDRESS_FAMILY_IPV4) {
+ ret = inet_pton(AF_INET, ip, buf);
+ } else if (addr_family == VINE_ADDRESS_FAMILY_IPV6) {
+ ret = inet_pton(AF_INET6, ip, buf);
+ } else {
+ ret |= inet_pton(AF_INET, ip, buf);
+ ret |= inet_pton(AF_INET6, ip, buf);
+ }
+
+ return (ret == 1);
+}
+
+static bool _check_topic_len(const char * topic)
+{
+ RET_VAL_IF(topic == NULL, false, "topic is NULL");
+ int len = strlen(topic);
+ return len > 0 && len <= VINE_MAX_TOPIC_LEN;
+}
+
+static void _received_cb(vine_data_path_h datapath, size_t received_len, void *user_data)
+{
+ if (!datapath || !user_data)
+ return;
+
+ static_cast<DataPath *>(user_data)->invoke_received_cb(received_len);
+}
+
+static void _terminated_cb(vine_data_path_h datapath, void *user_data)
+{
+ if (!datapath || !user_data)
+ return;
+
+ static_cast<DataPath *>(user_data)->invoke_terminated_cb();
+}
+
+static void _opened_cb(vine_data_path_h datapath, int result, int port, void *userdata)
+{
+ DataPath *dp = static_cast<DataPath *>(userdata);
+ dp->set_port(port);
+
+ VINE_LOGI("port[%d] result[%d]", port, result);
+ dp->invoke_opened_cb((vine_error_e)result);
+}
+
+static void _accepted_cb(vine_data_path_h datapath, void *user_data)
+{
+ if (!datapath || !user_data)
+ return;
+
+ void *event_fd = static_cast<DataPath *>(user_data)->get_eventfd();
+
+ // datapath is created newly. DP class should be needed for it.
+ // event_fd is the same as corresponding DPServer.
+ DPClient *connected_client_dp = new DPClient(event_fd, datapath);
+ _vine_data_path_set_received_cb(datapath,
+ _received_cb, static_cast<void *>(connected_client_dp));
+ vine_data_path_set_terminated_cb(datapath,
+ _terminated_cb, static_cast<void *>(connected_client_dp));
+ static_cast<DPServer *>(user_data)->invoke_accepted_cb(connected_client_dp);
+}
+
+static void _connected_cb(vine_data_path_h datapath, int result, void *user_data)
+{
+ _vine_data_path_set_received_cb(datapath, _received_cb, user_data);
+ vine_data_path_set_terminated_cb(datapath, _terminated_cb, user_data);
+ static_cast<DataPath *>(user_data)->invoke_opened_cb(result);
+}
+
+static void _pubsub_received_cb(vine_data_path_h datapath, size_t received_len, void *user_data)
+{
+ if (!datapath || !user_data || received_len == 0)
+ return;
+
+ VINE_LOGD("receive %zd bytes from datapath[%p]", received_len, datapath);
+
+ static_cast<DPPubSub *>(user_data)->noti_received_peer(datapath, received_len);
+ static_cast<DataPath *>(user_data)->invoke_received_cb(received_len);
+}
+
+static void _pubsub_terminated_cb(vine_data_path_h datapath, void *user_data)
+{
+ if (!datapath || !user_data)
+ return;
+
+ VINE_LOGD("datapath[%p] is terminated by peer.", datapath);
+ static_cast<DPPubSub *>(user_data)->del_joined_peer(datapath);
+ _vine_data_path_destroy(datapath);
+}
+
+static void _pubsub_opened_cb(vine_data_path_h datapath, int result, int port, void *userdata)
+{
+ if (!userdata)
+ return;
+
+ DPPubSub *dp = static_cast<DPPubSub *>(userdata);
+ dp->set_port(port);
+
+ VINE_LOGI("port[%d] result[%d]", port, result);
+
+ // Notify user that a listen socket cannot be used anymore.
+ if (result != VINE_ERROR_NONE) {
+ static_cast<DataPath *>(userdata)->invoke_opened_cb(result);
+ dp->close();
+ return;
+ }
+
+ int ret = dp->publish_service();
+ if (ret != VINE_ERROR_NONE) {
+ dp->close();
+ static_cast<DataPath *>(userdata)->invoke_opened_cb(ret);
+ return;
+ }
+
+ ret = dp->subscribe_service();
+ if (ret != VINE_ERROR_NONE) {
+ dp->close();
+ static_cast<DataPath *>(userdata)->invoke_opened_cb(ret);
+ return;
+ }
+}
+
+static void _pubsub_accepted_cb(vine_data_path_h datapath, void *user_data)
+{
+ if (!datapath || !user_data)
+ return;
+
+ DPPubSub *dp = static_cast<DPPubSub *>(user_data);
+ if (dp->get_max_conns() <= dp->get_joined_peer()) {
+ VINE_LOGE("The max connection limit is reached. Ignore [%p].", datapath);
+ dp->decrease_init_disc_num();
+ _vine_data_path_close(datapath);
+ return;
+ }
+
+ const char *ip = _vine_data_path_get_ip(datapath);
+ int port = _vine_data_path_get_port(datapath);
+ dp->add_joined_peer(ip, port, datapath);
+
+ _vine_data_path_set_received_cb(datapath, _pubsub_received_cb, user_data);
+ vine_data_path_set_terminated_cb(datapath, _pubsub_terminated_cb, user_data);
+
+ if (dp->get_open_state() == VINE_DP_PUBSUB_OPEN_STATE_WAIT
+ && dp->decrease_init_disc_num() <= 0) {
+ dp->set_open_state(VINE_DP_PUBSUB_OPEN_STATE_DONE);
+ static_cast<DataPath *>(user_data)->invoke_opened_cb(VINE_ERROR_NONE);
+ }
+}
+
+static void _pubsub_connected_cb(vine_data_path_h datapath, int result, void *user_data)
+{
+ if (!datapath || !user_data)
+ return;
+
+ DPPubSub *dp = static_cast<DPPubSub *>(user_data);
+
+ if (result != 0) {
+ VINE_LOGE("connect failure.");
+ dp->decrease_init_disc_num();
+ return;
+ }
+
+ const char *ip = _vine_data_path_get_ip(datapath);
+ int port = _vine_data_path_get_port(datapath);
+
+ dp->add_joined_peer(ip, port, datapath);
+
+ _vine_data_path_set_received_cb(datapath, _pubsub_received_cb, user_data);
+ vine_data_path_set_terminated_cb(datapath, _pubsub_terminated_cb, user_data);
+
+ if (dp->get_open_state() == VINE_DP_PUBSUB_OPEN_STATE_WAIT
+ && dp->decrease_init_disc_num() <= 0) {
+ dp->set_open_state(VINE_DP_PUBSUB_OPEN_STATE_DONE);
+ static_cast<DataPath *>(user_data)->invoke_opened_cb(VINE_ERROR_NONE);
+ }
+}
+
+// for pubsub
+static int __vine_set_discovered_service(vine_service_h service,
+ const char *service_type, const char *service_name,
+ const char *host_name, int port,
+ const map<string, string> &attr, const char *iface_name)
+{
+ int ret = VINE_ERROR_NONE;
+ ret = _vine_service_set_type(service, service_type);
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to set service type");
+ ret = _vine_service_set_name(service, service_name);
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to set service name");
+ ret = _vine_service_set_host_name(service, host_name);
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to set host name");
+ ret = _vine_service_set_port(service, port);
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to set port");
+
+ for (const auto &kv : attr)
+ _vine_service_add_attribute(service, kv.first.c_str(), kv.second.c_str());
+
+ ret = _vine_service_set_iface_name(service, iface_name);
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to set iface_name");
+
+ return VINE_ERROR_NONE;
+}
+
+static void _ip_resolved_cb(vine_disc_h disc, vine_service_h service, bool add,
+ const char *ip, vine_address_family_e address_family, void *user_data)
+{
+ if (!user_data || !add) {
+ VINE_LOGD("state: %s", add ? "add" : "remove");
+ return;
+ }
+
+ DPPubSub *dp = static_cast<DPPubSub *>(user_data);
+ int port = _vine_service_get_port(service);
+ if (dp->is_joined_peer(_vine_service_get_name(service), ip, port)) {
+ VINE_LOGD("%s:%d was already joined.", ip, port);
+ return;
+ }
+
+ VINE_LOGD("IP Resolved %s:%d", ip, port);
+
+ vine_address_family_e supported_addr_family = dp->get_addr_family();
+ if (supported_addr_family != VINE_ADDRESS_FAMILY_DEFAULT
+ && supported_addr_family != address_family) {
+ VINE_LOGD("address family is dismatched. peer type[%d]", address_family);
+ return;
+ }
+
+ auto attr = _vine_service_get_attributes(service);
+ auto it = attr.find(VINE_DP_PUBSUB_RANK_KEY);
+ if (it == attr.end()) {
+ VINE_LOGE("peer doens't have a rank.");
+ return;
+ }
+
+ if (dp->get_max_conns() > dp->get_joined_peer()
+ && dp->check_if_connect(it->second.c_str(), address_family, ip, port)) {
+ VINE_LOGD("Try to connect a peer(%s:%d)", ip, port);
+ dp->connect(ip, port);
+ }
+}
+
+static void _service_discovered_cb(vine_disc_h disc, bool available,
+ const char *service_type, const char *service_name,
+ const char *host_name, int port, const map<string, string> &attr,
+ const char *iface_name, int more_coming, void *user_data)
+{
+ VINE_LOGD("%s is discovered. %s",
+ service_name, available ? "available" : "not available");
+
+ if (!user_data || !available)
+ return;
+
+ vine_service_h service;
+ int ret = _vine_service_create(&service, false);
+ RET_IF(ret != VINE_ERROR_NONE, "Fail to create a service");
+
+ auto it = attr.find(VINE_DP_PUBSUB_RANK_KEY);
+ if (it == attr.end()) {
+ VINE_LOGE("peer doens't have a rank.");
+ _vine_service_destroy(service);
+ return;
+ }
+
+ ret = _vine_service_set_disc_handle(service, disc);
+ RET_IF(ret != VINE_ERROR_NONE, "Fail to set disc_handle");
+
+ ret = __vine_set_discovered_service(service,
+ service_type, service_name, host_name, port, attr, iface_name);
+ if (ret != VINE_ERROR_NONE) {
+ VINE_LOGE("Fail to set a service. error(%d)", ret);
+ _vine_service_destroy(service);
+ return;
+ }
+
+ DPPubSub *dp = static_cast<DPPubSub *>(user_data);
+ if (more_coming && dp->get_open_state() == VINE_DP_PUBSUB_OPEN_STATE_WAIT) {
+ VINE_LOGD("At least one more result is exist.");
+ dp->increase_init_disc_num();
+ }
+
+ ret = vine_disc_resolve_ip(disc, service,
+ _ip_resolved_cb, user_data,
+ (vine_event_queue_h)static_cast<DataPath *>(user_data)->get_eventfd());
+ RET_IF(ret != VINE_ERROR_NONE, "Fail to vine_disc_resolve_ip");
+}
+
+static void _service_published_cb(vine_disc_h disc,
+ const char *service_name, vine_error_e error, void *user_data)
+{
+ VINE_LOGD("%s publish request %s.",
+ service_name, error == VINE_ERROR_NONE ? "succeed" : "failed");
+
+ if (!user_data)
+ return;
+
+ DPPubSub *dp = static_cast<DPPubSub *>(user_data);
+ dp->set_service_name(service_name);
+}
+
+int DataPath::set_security(void *security)
+{
+ return _vine_security_clone(&mSecurity, security);
+}
+
+int DataPath::set_iface_name(const std::string &iface_name)
+{
+ mIfaceName = iface_name;
+ return VINE_ERROR_NONE;
+}
+
+void DataPath::invoke_opened_cb(int result)
+{
+ if (mOpenedCb)
+ mOpenedCb(static_cast<void *>(this), (vine_error_e)result, mOpenedCbData);
+
+ // called only once.
+ mOpenedCb = NULL;
+ mOpenedCbData = NULL;
+}
+
+int DataPath::set_received_cb(vine_dp_received_cb callback, void *user_data)
+{
+ mReceivedCb = callback;
+ mReceivedCbData = user_data;
+ return VINE_ERROR_NONE;
+}
+
+int DataPath::unset_received_cb()
+{
+ mReceivedCb = NULL;
+ mReceivedCbData = NULL;
+ return VINE_ERROR_NONE;
+}
+
+void DataPath::invoke_received_cb(size_t received_len)
+{
+ if (mReceivedCb)
+ mReceivedCb(static_cast<void *>(this), received_len, mReceivedCbData);
+}
+
+int DataPath::set_terminated_cb(vine_dp_terminated_cb callback, void *user_data)
+{
+ mTerminatedCb = callback;
+ mTerminatedCbData = user_data;
+ return VINE_ERROR_NONE;
+}
+
+int DataPath::unset_terminated_cb()
+{
+ mTerminatedCb = NULL;
+ mTerminatedCbData = NULL;
+ return VINE_ERROR_NONE;
+}
+
+void DataPath::invoke_terminated_cb()
+{
+ if (mTerminatedCb)
+ mTerminatedCb(static_cast<void *>(this), mTerminatedCbData);
+}
+
+DPServer::DPServer(void *event_fd)
+{
+ VINE_LOGD("DPServer[%p] is created.", this);
+ mEventFd = event_fd;
+ mSecurity = NULL;
+ mAddrFamily = VINE_ADDRESS_FAMILY_DEFAULT;
+ mIfaceName = "";
+ mDataPath = NULL;
+ mListenPort = 0;
+ mMaxConnNum = VINE_DP_DEFAULT_CONNECTIONS_NUM;
+ mAcceptedCb = NULL;
+ mAcceptedCbData = NULL;
+ mReceivedCb = NULL;
+ mReceivedCbData = NULL;
+ mOpenedCb = NULL;
+ mOpenedCbData = NULL;
+ mTerminatedCb = NULL;
+ mTerminatedCbData = NULL;
+}
+
+DPServer::~DPServer()
+{
+ VINE_LOGD("DPServer[%p] is deleted.", this);
+ _vine_security_destroy(mSecurity);
+ _vine_data_path_destroy(mDataPath);
+}
+
+int DPServer::set_addr_family(vine_address_family_e addr_family)
+{
+ mAddrFamily = addr_family;
+ return VINE_ERROR_NONE;
+}
+
+int DPServer::set_remote_ip(vine_address_family_e addr_family, const std::string &ip)
+{
+ return VINE_ERROR_INVALID_OPERATION;
+}
+
+int DPServer::set_port(int port)
+{
+ if (port < 0 || port > 65535)
+ return VINE_ERROR_INVALID_PARAMETER;
+
+ mListenPort = port;
+
+ return VINE_ERROR_NONE;
+}
+
+int DPServer::set_topic(std::string topic)
+{
+ return VINE_ERROR_INVALID_OPERATION;
+}
+
+int DPServer::set_max_connections(int max_conn)
+{
+ if (max_conn < 1 || max_conn > VINE_DP_MAX_CONNECTIONS_NUM)
+ return VINE_ERROR_INVALID_PARAMETER;
+
+ mMaxConnNum = max_conn;
+ return VINE_ERROR_NONE;
+}
+
+int DPServer::set_accepted_cb(vine_dp_accepted_cb callback, void *user_data)
+{
+ mAcceptedCb = callback;
+ mAcceptedCbData = user_data;
+ return VINE_ERROR_NONE;
+}
+
+int DPServer::unset_accepted_cb()
+{
+ mAcceptedCb = NULL;
+ mAcceptedCbData = NULL;
+ return VINE_ERROR_NONE;
+}
+
+void DPServer::invoke_accepted_cb(vine_dp_h dp)
+{
+ if (mAcceptedCb)
+ mAcceptedCb(static_cast<void *>(this), dp, mAcceptedCbData);
+}
+
+int DPServer::open(vine_dp_opened_cb callback, void *user_data)
+{
+ const char *iface_name = mIfaceName.size() > 0 ? mIfaceName.c_str() : NULL;
+
+ mOpenedCb = callback;
+ mOpenedCbData = user_data;
+
+ return vine_data_path_open(mAddrFamily, mListenPort, iface_name, mMaxConnNum, mSecurity,
+ _opened_cb, static_cast<void *>(this),
+ _accepted_cb, static_cast<void *>(this),
+ &mDataPath, mEventFd);
+}
+
+void DPServer::close()
+{
+ _vine_data_path_close(mDataPath);
+}
+
+int DPServer::send(unsigned char *buf, size_t len)
+{
+ return _vine_data_path_write(mDataPath, buf, len);
+}
+
+int DPServer::recv(unsigned char *buf, size_t buf_len, size_t *read_len)
+{
+ return _vine_data_path_read(mDataPath, buf, buf_len, read_len);
+}
+
+DPClient::DPClient(void *event_fd)
+{
+ VINE_LOGD("DPClient[%p] is created.", this);
+ mEventFd = event_fd;
+ mSecurity = NULL;
+ mAddrFamily = VINE_ADDRESS_FAMILY_DEFAULT;
+ mIfaceName = "";
+ mDataPath = NULL;
+ mServerIp = "";
+ mServerPort = 0;
+ mReceivedCb = NULL;
+ mReceivedCbData = NULL;
+ mOpenedCb = NULL;
+ mOpenedCbData = NULL;
+ mTerminatedCb = NULL;
+ mTerminatedCbData = NULL;
+}
+
+DPClient::DPClient(void *event_fd, void *datapath)
+{
+ VINE_LOGD("DPClient[%p] is created with datapath[%p]", this, datapath);
+ mEventFd = event_fd;
+ mSecurity = NULL;
+ mDataPath = datapath;
+ mAddrFamily = VINE_ADDRESS_FAMILY_IPV4;
+ mServerIp = "";
+ mServerPort = 0;
+ mReceivedCb = NULL;
+ mReceivedCbData = NULL;
+ mOpenedCb = NULL;
+ mOpenedCbData = NULL;
+ mTerminatedCb = NULL;
+ mTerminatedCbData = NULL;
+}
+
+DPClient::~DPClient()
+{
+ VINE_LOGD("DPClient[%p] is deleted.", this);
+ _vine_security_destroy(mSecurity);
+ _vine_data_path_destroy(mDataPath);
+}
+
+int DPClient::set_addr_family(vine_address_family_e addr_family)
+{
+ return VINE_ERROR_INVALID_OPERATION;
+}
+
+int DPClient::set_remote_ip(vine_address_family_e addr_family, const std::string &ip)
+{
+ if (!_check_if_valid_ip(addr_family, ip.c_str()))
+ return VINE_ERROR_INVALID_PARAMETER;
+
+ mServerIp = ip;
+ mAddrFamily = addr_family;
+
+ return VINE_ERROR_NONE;
+}
+
+int DPClient::set_port(int port)
+{
+ if (port <= 0 || port > 65535) // Do not allow 0
+ return VINE_ERROR_INVALID_PARAMETER;
+
+ mServerPort = port;
+
+ return VINE_ERROR_NONE;
+}
+
+int DPClient::set_topic(std::string topic)
+{
+ return VINE_ERROR_INVALID_OPERATION;
+}
+
+int DPClient::set_accepted_cb(vine_dp_accepted_cb callback, void *user_data)
+{
+ return VINE_ERROR_INVALID_OPERATION;
+}
+
+int DPClient::unset_accepted_cb()
+{
+ return VINE_ERROR_INVALID_OPERATION;
+}
+
+int DPClient::open(vine_dp_opened_cb callback, void *user_data)
+{
+ const char *iface_name = mIfaceName.size() > 0 ? mIfaceName.c_str() : NULL;
+
+ mOpenedCb = callback;
+ mOpenedCbData = user_data;
+
+ return vine_data_path_connect(mAddrFamily, mServerIp.c_str(), mServerPort,
+ iface_name, mSecurity, _connected_cb, static_cast<void *>(this), &mDataPath, mEventFd);
+}
+
+void DPClient::close()
+{
+ _vine_data_path_close(mDataPath);
+}
+
+int DPClient::send(unsigned char *buf, size_t len)
+{
+ return _vine_data_path_write(mDataPath, buf, len);
+}
+
+int DPClient::recv(unsigned char *buf, size_t buf_len, size_t *read_len)
+{
+ return _vine_data_path_read(mDataPath, buf, buf_len, read_len);
+}
+
+DPPubSub::DPPubSub(void *event_fd)
+{
+ VINE_LOGD("DPPubSub[%p] is created.", this);
+ mEventFd = event_fd;
+ mSecurity = NULL;
+ mIfaceName = "";
+ mReceivedCb = NULL;
+ mReceivedCbData = NULL;
+ mOpenedCb = NULL;
+ mOpenedCbData = NULL;
+ mTerminatedCb = NULL;
+ mTerminatedCbData = NULL;
+
+ // Discovery Info
+ mTopic = "";
+ mSdSub = NULL;
+ mSdPub = NULL;
+ mServiceName = "";
+ mInitDiscNum = 1;
+ mOpenState = VINE_DP_PUBSUB_OPEN_STATE_NONE;
+
+ // Network Info
+ mAddrFamily = VINE_ADDRESS_FAMILY_DEFAULT;
+ mListenPort = 0;
+ mMaxConnNum = VINE_DP_DEFAULT_CONNECTIONS_NUM;
+ mServerDataPath = NULL;
+ mRank = 0;
+}
+
+DPPubSub::~DPPubSub()
+{
+ VINE_LOGD("DPPubSub[%p] is deleted.", this);
+ _vine_security_destroy(mSecurity);
+ close();
+}
+
+int DPPubSub::set_addr_family(vine_address_family_e addr_family)
+{
+ mAddrFamily = addr_family;
+ return VINE_ERROR_NONE;
+}
+
+int DPPubSub::set_remote_ip(vine_address_family_e addr_family, const std::string &ip)
+{
+ return VINE_ERROR_INVALID_OPERATION;
+}
+
+int DPPubSub::set_port(int port)
+{
+ if (port < 0 || port > 65535)
+ return VINE_ERROR_INVALID_PARAMETER;
+
+ mListenPort = port;
+
+ return VINE_ERROR_NONE;
+}
+
+int DPPubSub::set_topic(std::string topic)
+{
+ mTopic = topic;
+ return VINE_ERROR_NONE;
+}
+
+int DPPubSub::set_max_connections(int max_conn)
+{
+ if (max_conn < 1 || max_conn > VINE_DP_MAX_CONNECTIONS_NUM)
+ return VINE_ERROR_INVALID_PARAMETER;
+
+ mMaxConnNum = max_conn;
+ return VINE_ERROR_NONE;
+}
+
+int DPPubSub::set_accepted_cb(vine_dp_accepted_cb callback, void *user_data)
+{
+ return VINE_ERROR_INVALID_OPERATION;
+}
+
+int DPPubSub::unset_accepted_cb()
+{
+ return VINE_ERROR_INVALID_OPERATION;
+}
+
+int DPPubSub::connect(const char *ip, int port)
+{
+ vine_data_path_h datapath;
+ const char *iface_name = mIfaceName.size() > 0 ? mIfaceName.c_str() : NULL;
+ int ret = vine_data_path_connect(mAddrFamily, ip, port, iface_name, mSecurity,
+ _pubsub_connected_cb, static_cast<void *>(this), &datapath, mEventFd);
+ return ret;
+}
+
+int DPPubSub::close_server_dp()
+{
+ int ret = _vine_data_path_close(mServerDataPath);
+ mServerDataPath = NULL;
+ return ret;
+}
+
+int DPPubSub::publish_service()
+{
+ vine_service_h service;
+ int ret;
+ char rank_str[VINE_DP_PUBSUB_RANK_LEN] = {0 , };
+
+ ret = vine_service_create(&service);
+ if (ret != VINE_ERROR_NONE)
+ return ret;
+
+ vine_service_set_type(service, mTopic.c_str());
+ vine_service_set_port(service, mListenPort);
+
+ mRank = create_rank();
+ sprintf(rank_str, "%d", mRank);
+ vine_service_add_attribute(service, VINE_DP_PUBSUB_RANK_KEY, (const char *)rank_str);
+
+ if (mSdPub == NULL) {
+ ret = vine_disc_create(VINE_DISCOVERY_METHOD_DNS_SD, &mSdPub);
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to vine_disc_create");
+ }
+ ret = vine_disc_publish(mSdPub,
+ service, NULL,
+ _service_published_cb, static_cast<void *>(this),
+ mEventFd);
+ if (ret != VINE_ERROR_NONE) {
+ vine_disc_destroy(mSdPub);
+ mSdPub = NULL;
+ } else {
+ mSdPubSubState |= VINE_DP_PUBSUB_SD_STATE_PUBLISH;
+ }
+ vine_service_destroy(service);
+
+ VINE_LOGD("Publish %s:%d with rank %d", mTopic.c_str(), mListenPort, mRank);
+ return ret;
+}
+
+int DPPubSub::subscribe_service()
+{
+ int ret;
+
+ if (mSdSub == NULL) {
+ ret = vine_disc_create(VINE_DISCOVERY_METHOD_DNS_SD, &mSdSub);
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to vine_disc_create");
+ }
+
+ ret = vine_disc_subscribe(mSdSub,
+ mTopic.c_str(), NULL,
+ _service_discovered_cb, static_cast<void *>(this),
+ mEventFd);
+ if (ret != VINE_ERROR_NONE) {
+ vine_disc_destroy(mSdSub);
+ mSdSub = NULL;
+ } else {
+ mSdPubSubState |= VINE_DP_PUBSUB_SD_STATE_SUBSCRIBE;
+ }
+
+ return ret;
+}
+
+int DPPubSub::create_rank()
+{
+ srand(time(NULL));
+ return rand() % VINE_DP_PUBSUB_RANK_MAX;
+}
+
+int DPPubSub::open(vine_dp_opened_cb callback, void *user_data)
+{
+ if (mOpenState == VINE_DP_PUBSUB_OPEN_STATE_WAIT) {
+ VINE_LOGE("Ignore duplicate request.");
+ return VINE_ERROR_NOW_IN_PROGRESS;
+ } else if (mOpenState == VINE_DP_PUBSUB_OPEN_STATE_DONE) {
+ VINE_LOGE("Already opened.");
+ return VINE_ERROR_INVALID_OPERATION;
+ }
+
+ mOpenState = VINE_DP_PUBSUB_OPEN_STATE_WAIT;
+ mOpenTimer.start(VINE_DP_PUBSUB_OPEN_TIMEOUT_MS, _open_timer, static_cast<void *>(this));
+
+ mOpenedCb = callback;
+ mOpenedCbData = user_data;
+
+ const char *iface_name = mIfaceName.size() > 0 ? mIfaceName.c_str() : NULL;
+ int ret = vine_data_path_open(mAddrFamily, mListenPort, iface_name, mMaxConnNum, mSecurity,
+ _pubsub_opened_cb, static_cast<void *>(this),
+ _pubsub_accepted_cb, static_cast<void *>(this),
+ &mServerDataPath, mEventFd);
+ if (ret != VINE_ERROR_NONE) {
+ mOpenedCb = NULL;
+ mOpenedCbData = NULL;
+ mOpenTimer.stop();
+ return ret;
+ }
+
+ return VINE_ERROR_NONE;
+}
+
+void DPPubSub::close()
+{
+ mOpenTimer.stop();
+ vine_disc_stop_publish(mSdPub);
+ vine_disc_stop_subscribe(mSdSub);
+
+ vine_disc_destroy(mSdSub);
+ mSdSub = NULL;
+ vine_disc_destroy(mSdPub);
+ mSdPub = NULL;
+
+ clear_joined_peer();
+ _vine_data_path_close(mServerDataPath);
+ _vine_data_path_destroy(mServerDataPath);
+ mServerDataPath = NULL;
+
+ mOpenState = VINE_DP_PUBSUB_OPEN_STATE_NONE;
+ mSdPubSubState = VINE_DP_PUBSUB_SD_STATE_NONE;
+}
+
+int DPPubSub::send(unsigned char *buf, size_t len)
+{
+ int ret = VINE_ERROR_NONE;
+
+ for (auto &peer : mDataPathList) {
+ if (!peer.second)
+ continue;
+ ret = _vine_data_path_write(peer.second, buf, len);
+ if (ret != VINE_ERROR_NONE) {
+ VINE_LOGE("fail to write to a peer[%p] ", peer.second);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int DPPubSub::recv(unsigned char *buf, size_t buf_len, size_t *read_len)
+{
+ auto &dp_info = mRecvDataPathList.front();
+ size_t bytes = 0;
+ int ret = _vine_data_path_read(dp_info.first, buf, buf_len, &bytes);
+ if (ret != VINE_ERROR_NONE)
+ return ret;
+
+ dp_info.second -= bytes;
+ VINE_LOGD("%zd bytes remained", dp_info.second);
+
+ *read_len = bytes;
+ if (dp_info.second == 0)
+ mRecvDataPathList.erase();
+
+ return VINE_ERROR_NONE;
+}
+
+bool DPPubSub::is_joined_peer(const char *service_name, const char *ip, int port)
+{
+ if (mServiceName.compare(service_name) == 0) {
+ VINE_LOGD("It's me!");
+ mIpList.insert(ip);
+ return true;
+ }
+
+ // TODO: compare the service name.
+ // service name might be unique.
+
+ DPKey key = std::make_pair(ip, port);
+ return (mDataPathList.count(key) > 0);
+}
+
+int DPPubSub::get_joined_peer()
+{
+ return mDataPathList.size();
+}
+
+void DPPubSub::add_joined_peer(const char *ip, int port, vine_data_path_h datapath)
+{
+ if (!ip || !datapath)
+ return;
+
+ VINE_LOGD("%s:%d, %p is added.", ip, port, datapath);
+ DPKey key = std::make_pair(ip, port);
+ mDataPathList.insert(std::make_pair(key, datapath));
+}
+
+void DPPubSub::del_joined_peer(vine_data_path_h datapath)
+{
+ if (!datapath)
+ return;
+
+ DPMap::iterator found = std::find_if(mDataPathList.begin(),
+ mDataPathList.end(),
+ [datapath](std::pair<DPKey, vine_data_path_h> const& item)
+ {
+ VINE_LOGD("item.second[%p]", item.second);
+ return (item.second == datapath);
+ });
+
+ if (found == mDataPathList.end()) {
+ VINE_LOGE("Cannot find the datapath[%p].", datapath);
+ return;
+ }
+
+ VINE_LOGD("datapath[%p] is deleted from list.", datapath);
+ mDataPathList.erase(found);
+}
+
+void DPPubSub::clear_joined_peer()
+{
+ for (auto &peer : mDataPathList) {
+ if (!peer.second)
+ continue;
+ _vine_data_path_close(peer.second);
+ _vine_data_path_destroy(peer.second);
+ }
+
+ mDataPathList.clear();
+}
+
+void DPPubSub::noti_received_peer(vine_data_path_h datapath, size_t bytes)
+{
+ mRecvDataPathList.push(std::make_pair(datapath, bytes));
+}
+
+#if 0
+static int _get_last_two_octets(string ip)
+{
+ size_t pos = ip.find_last_of(':');
+ string sub = ip.substr(pos + 1);
+
+ if (sub.size() == 0) {
+ VINE_LOGE("invalid IP.");
+ return -1;
+ }
+
+ return std::stoi(sub, NULL, 16);
+}
+#endif
+
+// The smaller the last octet, the higher the priority.
+// 1: peers priority is higher than me
+// 0: same or failure
+// -1: peers priority is lower than me
+int DPPubSub::compare_ip_priority(const char *peer_ip)
+{
+ std::string peer_ip_str(peer_ip);
+ size_t pos = peer_ip_str.find_last_of('.');
+ std::string subnet = peer_ip_str.substr(0, pos);
+ std::string found_ip;
+ int last = 0, peer_last = 0;
+
+ if (subnet.size() == 0)
+ return 0;
+
+ for (auto &ip : mIpList) {
+ if (ip.find(subnet) != 0)
+ continue;
+
+ found_ip = ip;
+ break;
+ }
+
+ VINE_LOGD("peer_ip[%s] found_ip[%s]", peer_ip, found_ip.c_str());
+ try {
+ last = std::stoi(found_ip.substr(pos + 1));
+ peer_last = std::stoi(peer_ip_str.substr(pos + 1));
+ } catch (...) {
+ VINE_LOGD("Failed to convert last octet. IP is invalid.");
+ }
+
+ if (last == peer_last)
+ return 0;
+ return (last < peer_last ? 1 : -1);
+}
+
+// If true is return, it will connect to a peer.
+bool DPPubSub::check_if_connect(const char *peer_rank,
+ vine_address_family_e ip_type, const char *peer_ip, int peer_port)
+{
+ // The smaller the rank value, the higher the priority.
+ // Connect to a peer when both 1 and 2 are satisfied.
+ // 1. peer has rank key and value
+ // 2. the rank value is higher than peers value
+ int prank = std::stoi(peer_rank);
+
+ VINE_LOGD("ip_type[%d] rank[%d], Peer rank[%d] peer_ip[%s] peer_port[%d]",
+ ip_type, mRank, prank, peer_ip, peer_port);
+ if (mRank > prank)
+ return true;
+ else if (mRank < prank)
+ return false;
+
+ // If rank is the same, Need to check the IP address
+ int is_higher = 0;
+ if (ip_type == VINE_ADDRESS_FAMILY_IPV4) {
+ is_higher = compare_ip_priority(peer_ip);
+ } else {
+ // TODO : handle IPv6 address
+ }
+
+ if (is_higher == 1)
+ return true;
+
+ // If last value of IP is the same, Need to check the port
+ if (mListenPort > peer_port)
+ return true;
+
+ return false;
+}
+
+void DPPubSub::_open_timer(void *user_data)
+{
+ VINE_LOGD("open timeout reached.");
+ if (!user_data)
+ return;
+
+ DPPubSub *dp = static_cast<DPPubSub *>(user_data);
+ int sd_state = dp->get_sd_pubsub_state();
+ if ((sd_state & VINE_DP_PUBSUB_SD_STATE_PUBLISH)
+ && (sd_state & VINE_DP_PUBSUB_SD_STATE_SUBSCRIBE)) {
+ dp->set_open_state(VINE_DP_PUBSUB_OPEN_STATE_DONE);
+ static_cast<DataPath *>(user_data)->invoke_opened_cb(VINE_ERROR_NONE);
+ return;
+ }
+ static_cast<DataPath *>(user_data)->invoke_opened_cb(VINE_ERROR_OPERATION_FAILED);
+}
+
+int _vine_dp_create(vine_session_h session, vine_dp_type_e type, vine_dp_h *dp)
+{
+ RET_VAL_IF(session == NULL, VINE_ERROR_INVALID_PARAMETER, "session is null.");
+ RET_VAL_IF(type >= VINE_DP_TYPE_UNKNOWN, VINE_ERROR_INVALID_PARAMETER, "invalid type");
+ RET_VAL_IF(dp == NULL, VINE_ERROR_INVALID_PARAMETER, "dp is null.");
+
+ vine_event_queue_h eq = NULL;
+ int ret = _vine_session_get_event_queue(session, &eq);
+
+ if (ret != VINE_ERROR_NONE || !eq)
+ return VINE_ERROR_INVALID_PARAMETER;
+
+ if (type == VINE_DP_TYPE_SERVER) {
+ *dp = new DPServer((void *)eq);
+ } else if (type == VINE_DP_TYPE_CLIENT) {
+ *dp = new DPClient((void *)eq);
+ } else if (type == VINE_DP_TYPE_PUBSUB) {
+ *dp = new DPPubSub((void *)eq);
+ }
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_dp_destroy(vine_dp_h dp)
+{
+ RET_VAL_IF(dp == NULL, VINE_ERROR_INVALID_PARAMETER, "dp is null.");
+
+ DataPath *_dp = static_cast<DataPath *>(dp);
+ delete _dp;
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_dp_set_iface_name(vine_dp_h dp, const char *iface_name)
+{
+ RET_VAL_IF(dp == NULL, VINE_ERROR_INVALID_PARAMETER, "dp is null.");
+ RET_VAL_IF(iface_name == NULL, VINE_ERROR_INVALID_PARAMETER, "iface_name is null.");
+
+ DataPath *_dp = static_cast<DataPath *>(dp);
+ return _dp->set_iface_name(iface_name);
+}
+
+int _vine_dp_set_addr_family(vine_dp_h dp, vine_address_family_e addr_family)
+{
+ RET_VAL_IF(dp == NULL, VINE_ERROR_INVALID_PARAMETER, "dp is null.");
+ RET_VAL_IF(addr_family < VINE_ADDRESS_FAMILY_DEFAULT
+ || addr_family > VINE_ADDRESS_FAMILY_IPV6,
+ VINE_ERROR_INVALID_PARAMETER, "addr_family is invalid.");
+
+ DataPath *_dp = static_cast<DataPath *>(dp);
+ return _dp->set_addr_family(addr_family);
+}
+
+int _vine_dp_set_remote_ip(vine_dp_h dp, vine_address_family_e addr_family, const char *ip)
+{
+ RET_VAL_IF(dp == NULL, VINE_ERROR_INVALID_PARAMETER, "dp is null.");
+ RET_VAL_IF(ip == NULL, VINE_ERROR_INVALID_PARAMETER, "ip is null.");
+
+ DataPath *_dp = static_cast<DataPath *>(dp);
+ return _dp->set_remote_ip(addr_family, ip);
+}
+
+int _vine_dp_set_port(vine_dp_h dp, int port)
+{
+ RET_VAL_IF(dp == NULL, VINE_ERROR_INVALID_PARAMETER, "dp is null.");
+
+ DataPath *_dp = static_cast<DataPath *>(dp);
+ return _dp->set_port(port);
+}
+
+int _vine_dp_get_port(vine_dp_h dp, int *port)
+{
+ RET_VAL_IF(dp == NULL, VINE_ERROR_INVALID_PARAMETER, "dp is null.");
+ RET_VAL_IF(port == NULL, VINE_ERROR_INVALID_PARAMETER, "port is null.");
+
+ DataPath *_dp = static_cast<DataPath *>(dp);
+ *port = _dp->get_port();
+ return VINE_ERROR_NONE;
+}
+
+int _vine_dp_set_topic(vine_dp_h dp, const char *topic)
+{
+ RET_VAL_IF(dp == NULL, VINE_ERROR_INVALID_PARAMETER, "dp is null.");
+ RET_VAL_IF(topic == NULL, VINE_ERROR_INVALID_PARAMETER, "topic is null.");
+ RET_VAL_IF(_check_topic_len(topic) == false,
+ VINE_ERROR_INVALID_PARAMETER, "invalid length of topic.");
+
+ DataPath *_dp = static_cast<DataPath *>(dp);
+ return _dp->set_topic(topic);
+}
+
+int _vine_dp_set_max_connections(vine_dp_h dp, int max_conn)
+{
+ RET_VAL_IF(dp == NULL, VINE_ERROR_INVALID_PARAMETER, "dp is null.");
+
+ DataPath *_dp = static_cast<DataPath *>(dp);
+ return _dp->set_max_connections(max_conn);
+}
+
+int _vine_dp_set_security(vine_dp_h dp, vine_security_h security)
+{
+ RET_VAL_IF(dp == NULL, VINE_ERROR_INVALID_PARAMETER, "dp is null.");
+ RET_VAL_IF(security == NULL, VINE_ERROR_INVALID_PARAMETER, "security is null.");
+
+ DataPath *_dp = static_cast<DataPath *>(dp);
+ return _dp->set_security(security);
+}
+
+int _vine_dp_set_accepted_cb(vine_dp_h dp, vine_dp_accepted_cb callback, void *user_data)
+{
+ RET_VAL_IF(dp == NULL, VINE_ERROR_INVALID_PARAMETER, "dp is null.");
+ RET_VAL_IF(callback == NULL, VINE_ERROR_INVALID_PARAMETER, "callback is null.");
+
+ DataPath *_dp = static_cast<DataPath *>(dp);
+ return _dp->set_accepted_cb(callback, user_data);
+}
+
+int _vine_dp_unset_accepted_cb(vine_dp_h dp)
+{
+ RET_VAL_IF(dp == NULL, VINE_ERROR_INVALID_PARAMETER, "dp is null.");
+
+ DataPath *_dp = static_cast<DataPath *>(dp);
+ return _dp->unset_accepted_cb();
+}
+
+int _vine_dp_set_terminated_cb(vine_dp_h dp, vine_dp_terminated_cb callback, void *user_data)
+{
+ RET_VAL_IF(dp == NULL, VINE_ERROR_INVALID_PARAMETER, "dp is null.");
+ RET_VAL_IF(callback == NULL, VINE_ERROR_INVALID_PARAMETER, "callback is null.");
+
+ DataPath *_dp = static_cast<DataPath *>(dp);
+ return _dp->set_terminated_cb(callback, user_data);
+}
+
+int _vine_dp_unset_terminated_cb(vine_dp_h dp)
+{
+ RET_VAL_IF(dp == NULL, VINE_ERROR_INVALID_PARAMETER, "dp is null.");
+
+ DataPath *_dp = static_cast<DataPath *>(dp);
+ return _dp->unset_terminated_cb();
+}
+
+int _vine_dp_open(vine_dp_h dp, vine_dp_opened_cb callback, void *user_data)
+{
+ RET_VAL_IF(dp == NULL, VINE_ERROR_INVALID_PARAMETER, "dp is null.");
+ RET_VAL_IF(callback == NULL, VINE_ERROR_INVALID_PARAMETER, "callback is null.");
+
+ DataPath *_dp = static_cast<DataPath *>(dp);
+ return _dp->open(callback, user_data);
+}
+
+int _vine_dp_close(vine_dp_h dp)
+{
+ RET_VAL_IF(dp == NULL, VINE_ERROR_INVALID_PARAMETER, "dp is null.");
+
+ DataPath *_dp = static_cast<DataPath *>(dp);
+ _dp->close();
+ return VINE_ERROR_NONE;
+}
+
+int _vine_dp_send(vine_dp_h dp, unsigned char *buf, size_t len)
+{
+ RET_VAL_IF(dp == NULL, VINE_ERROR_INVALID_PARAMETER, "dp is null.");
+ RET_VAL_IF(buf == NULL, VINE_ERROR_INVALID_PARAMETER, "buf is null.");
+
+ DataPath *_dp = static_cast<DataPath *>(dp);
+ return _dp->send(buf, len);
+}
+
+int _vine_dp_recv(vine_dp_h dp, unsigned char *buf, size_t buf_len, size_t *read_len)
+{
+ RET_VAL_IF(dp == NULL, VINE_ERROR_INVALID_PARAMETER, "dp is null.");
+ RET_VAL_IF(buf == NULL, VINE_ERROR_INVALID_PARAMETER, "buf is null.");
+ RET_VAL_IF(read_len == NULL, VINE_ERROR_INVALID_PARAMETER, "read_len is null.");
+
+ DataPath *_dp = static_cast<DataPath *>(dp);
+ return _dp->recv(buf, buf_len, read_len);
+}
+
+int _vine_dp_set_received_cb(vine_dp_h dp, vine_dp_received_cb callback, void *user_data)
+{
+ RET_VAL_IF(dp == NULL, VINE_ERROR_INVALID_PARAMETER, "dp is null.");
+ RET_VAL_IF(callback== NULL, VINE_ERROR_INVALID_PARAMETER, "callback is null.");
+
+ DataPath *_dp = static_cast<DataPath *>(dp);
+ return _dp->set_received_cb(callback, user_data);
+}
+
+int _vine_dp_unset_received_cb(vine_dp_h dp)
+{
+ RET_VAL_IF(dp == NULL, VINE_ERROR_INVALID_PARAMETER, "dp is null.");
+
+ DataPath *_dp = static_cast<DataPath *>(dp);
+ return _dp->unset_received_cb();
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+
+#include <sys/eventfd.h>
+#include <unistd.h>
+#include <stdint.h>
+
+#include "vine.h"
+#include "vine-event-loop.h"
+#include "vine-queue.h"
+#include "vine-log.h"
+#include "vine-utils.h"
+
+static pthread_t __vine_event_loop_tid = 0;
+
+static bool __cleanup = false;
+static int __vine_epoll_fd = 0;
+//static struct epoll_event __vine_epoll_events[MAX_VINE_EPOLL_EVENTS];
+//static int __vine_epoll_events_idx = 0;
+
+typedef struct {
+ int fd;
+ vine_poll_handler handler;
+ void *user_data;
+} vine_io_event_handler;
+
+typedef struct {
+ void *event_data;
+ vine_event_handler handler;
+ vine_event_free_handler free_func;
+ void *user_data;
+} vine_event;
+
+typedef struct {
+ int fd;
+ VineQueue<vine_event *> event_queue;
+} vine_event_queue_s;
+
+#ifdef FD_SETSIZE
+#define MAX_IO_EVENT_HANDLERS FD_SETSIZE
+#else
+#define MAX_IO_EVENT_HANDLERS MAX_VINE_EPOLL_EVENTS
+#endif
+
+static vine_io_event_handler *io_event_handlers[MAX_IO_EVENT_HANDLERS] = {0, };
+
+static void *__vine_event_loop_run(void *arg)
+{
+ VINE_LOGD("Run Vine event loop");
+
+ struct epoll_event events[MAX_VINE_EPOLL_EVENTS];
+ // TODO: Do we have to use timeout?
+ int timeout = 0;
+
+ do {
+ int n = epoll_wait(__vine_epoll_fd, events, MAX_VINE_EPOLL_EVENTS, timeout);
+ if (n == -1) {
+ VINE_LOGE("Invalid return value");
+ break;
+ }
+
+ for (int i = 0; i < n; ++i) {
+ vine_io_event_handler *h = (vine_io_event_handler *)events[i].data.ptr;
+ if (h && h->handler)
+ h->handler(h->fd, events[i].events, h->user_data);
+ }
+ } while (!__cleanup);
+
+ // TODO:
+ // Even although __cleanup is set as false,
+ // this loop cannot be broken if waiting epoll_wait()
+ // --> socketpair()? timeout?
+
+ return NULL;
+}
+
+int vine_event_loop_init()
+{
+ __vine_epoll_fd = epoll_create1(0);
+ if (__vine_epoll_fd == -1) {
+ VINE_LOGE("Fail to create epoll fd. error %d", errno);
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+
+ __cleanup = false;
+
+ return VINE_ERROR_NONE;
+}
+
+void vine_event_loop_deinit()
+{
+ close(__vine_epoll_fd);
+}
+
+int vine_event_loop_start()
+{
+ VINE_LOGD("vine_event_loop_start");
+ if (__vine_event_loop_tid) {
+ VINE_LOGD("Vine event loop was already executed");
+ return VINE_ERROR_NONE;
+ }
+
+ if (pthread_create(&__vine_event_loop_tid, NULL,
+ __vine_event_loop_run, NULL) != 0) {
+ VINE_LOGE("Fail to create event thread");
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+
+ // pthread_detach(__vine_event_loop_tid);
+
+ return VINE_ERROR_NONE;
+}
+
+void vine_event_loop_stop()
+{
+ VINE_LOGD("vine_event_loop_stop");
+ __cleanup = true;
+ pthread_join(__vine_event_loop_tid, NULL);
+ __vine_event_loop_tid = 0;
+}
+
+int vine_event_queue_create(vine_event_queue_h *event_fd)
+{
+ vine_event_queue_s *event_fd_handle = new vine_event_queue_s;
+ VINE_LOGD("New Event Loop handle[%p]", event_fd_handle);
+
+ *event_fd = event_fd_handle;
+ event_fd_handle->fd = eventfd(0, 0);
+ if (event_fd_handle->fd == -1) {
+ VINE_LOGE("Fail to create eventfd. error %d", errno);
+ delete event_fd_handle;
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+ VINE_LOGD("Event FD: %d", event_fd_handle->fd);
+
+ return VINE_ERROR_NONE;
+}
+
+void vine_event_queue_destroy(vine_event_queue_h event_fd)
+{
+ vine_event_queue_s *event_fd_handle = (vine_event_queue_s *)event_fd;
+
+ //g_async_queue_unref(event_fd_handle->event_queue);
+ if (event_fd_handle->fd >= 0)
+ close(event_fd_handle->fd);
+ free(event_fd);
+}
+
+void vine_event_loop_get_eventfd(vine_event_queue_h event_fd, int *eventfd)
+{
+ vine_event_queue_s *event_fd_handle = (vine_event_queue_s *)event_fd;
+ *eventfd = event_fd_handle->fd;
+}
+
+static void _add_io_event_handler(int fd, vine_io_event_handler *h)
+{
+ if (io_event_handlers[fd])
+ free(io_event_handlers[fd]);
+ io_event_handlers[fd] = h;
+}
+
+static void _del_io_event_handler(int fd)
+{
+ if (io_event_handlers[fd]) {
+ free(io_event_handlers[fd]);
+ io_event_handlers[fd] = NULL;
+ }
+}
+
+static vine_io_event_handler *_find_io_event_handler(int fd)
+{
+ return io_event_handlers[fd];
+}
+
+int vine_event_loop_add_io_handler(
+ int fd, int events, vine_poll_handler handler, void *user_data)
+{
+ RET_VAL_IF(fd < 0, VINE_ERROR_INVALID_PARAMETER,
+ "fd should be equal to or greater than zero");
+
+ vine_io_event_handler *h =
+ (vine_io_event_handler *)calloc(1, sizeof(vine_io_event_handler));
+ RET_VAL_IF(h == NULL, VINE_ERROR_OUT_OF_MEMORY, "Out of memory");
+ h->fd = fd;
+ h->handler = handler;
+ h->user_data = user_data;
+ _add_io_event_handler(fd, h);
+
+ struct epoll_event event;
+ event.events = events;
+ event.data.ptr = (void *)h;
+
+ epoll_ctl(__vine_epoll_fd, EPOLL_CTL_ADD, fd, &event);
+ VINE_LOGD("Add an epoll event. fd: %d", fd);
+
+ return VINE_ERROR_NONE;
+}
+
+int vine_event_loop_mod_io_handler(
+ int fd, int events, vine_poll_handler handler, void *user_data)
+{
+ RET_VAL_IF(fd < 0, VINE_ERROR_INVALID_PARAMETER,
+ "fd should be equal to or greater than zero");
+
+ struct epoll_event event;
+ event.events = events;
+ event.data.ptr = _find_io_event_handler(fd);
+
+ epoll_ctl(__vine_epoll_fd, EPOLL_CTL_MOD, fd, &event);
+ VINE_LOGD("Modify an epoll event. fd: %d event: %d", fd, events);
+
+ return VINE_ERROR_NONE;
+}
+
+int vine_event_loop_del_io_handler(int fd)
+{
+ RET_VAL_IF(fd < 0, VINE_ERROR_INVALID_PARAMETER,
+ "fd should be equal to or greater than zero");
+
+ epoll_ctl(__vine_epoll_fd, EPOLL_CTL_DEL, fd, NULL);
+ _del_io_event_handler(fd);
+ VINE_LOGD("Del an epoll event. fd: %d", fd);
+
+ return VINE_ERROR_NONE;
+}
+
+// Register an event handler which is called in vine_event_loop_process()
+// And then, an appropriate user callback will be called by the handler.
+int vine_event_loop_add_event(vine_event_queue_h event_fd, void *event_data,
+ vine_event_handler handler, vine_event_free_handler free_func,
+ void *user_data)
+{
+ VINE_LOGD("event_data[%p] event_fd[%p]", event_data, event_fd);
+ vine_event_queue_s *event_fd_handle = (vine_event_queue_s *)event_fd;
+ RET_VAL_IF(event_fd == NULL, VINE_ERROR_INVALID_OPERATION, "event_fd is NULL");
+
+ vine_event *event = (vine_event *)calloc(1, sizeof(vine_event));
+ RET_VAL_IF(event == NULL, VINE_ERROR_OUT_OF_MEMORY, "Out of memory");
+
+ event->event_data = event_data;
+ event->handler = handler;
+ event->free_func = free_func;
+ event->user_data = user_data;
+ VINE_LOGD("Vine event[%p]", event);
+
+ event_fd_handle->event_queue.push(event);
+
+ uint64_t u = 1;
+ if (write(event_fd_handle->fd, &u, sizeof(uint64_t)) == -1) {
+ VINE_LOGE("Write error(%d)", errno);
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+
+ return VINE_ERROR_NONE;
+}
+
+int vine_event_loop_process(vine_event_queue_h event_fd)
+{
+ VINE_LOGD("Process a vine event. event_fd[%p]", event_fd);
+ vine_event_queue_s *event_fd_handle = (vine_event_queue_s *)event_fd;
+ RET_VAL_IF(event_fd == NULL, VINE_ERROR_INVALID_OPERATION, "event_fd is NULL");
+
+ uint64_t u;
+ if (read(event_fd_handle->fd, &u, sizeof(uint64_t)) != sizeof(uint64_t)) {
+ VINE_LOGE("Read error(%d)", errno);
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+
+ VINE_LOGD("eventfd counter: %ld", u);
+
+ while (u--) {
+ vine_event *event = event_fd_handle->event_queue.pop();
+ if (event == NULL) {
+ VINE_LOGE("vine event queue is empty");
+ return VINE_ERROR_INVALID_OPERATION;
+ }
+
+ VINE_LOGD("Vine event[%p]", event);
+ if (event->handler == NULL) {
+ VINE_LOGI("No event handler");
+ return VINE_ERROR_NONE;
+ }
+
+ event->handler(event->event_data, event->user_data);
+ }
+
+ return VINE_ERROR_NONE;
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+
+#include "vine.h"
+#include "vine-data-path.h"
+#include "vine-disc.h"
+#include "vine-dp.h"
+#include "vine-event-loop.h"
+#include "vine-log.h"
+#include "vine-private.h"
+#include "vine-utils.h"
+
+static int __vine_ref_count = 0;
+static pthread_mutex_t __vine_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static bool __vine_is_initialized()
+{
+ return __vine_ref_count > 0;
+}
+
+static int __vine_ref()
+{
+ __vine_ref_count++;
+ VINE_LOGD("__vine_ref_count: %d", __vine_ref_count);
+ return __vine_ref_count;
+}
+
+static int __vine_unref()
+{
+ __vine_ref_count--;
+ VINE_LOGD("__vine_ref_count: %d", __vine_ref_count);
+ return __vine_ref_count;
+}
+
+int _vine_init()
+{
+ VINE_LOGD("_vine_init");
+ VINE_LOCK(&__vine_mutex);
+ if (!__vine_is_initialized()) {
+ if (vine_disc_init()) {
+ VINE_LOGE("Fail to init vine discovery");
+ VINE_UNLOCK(&__vine_mutex);
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+
+ if (vine_data_path_init()) {
+ VINE_LOGE("Fail to init vine data path");
+ vine_disc_deinit();
+ VINE_UNLOCK(&__vine_mutex);
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+
+ if (vine_event_loop_init()) {
+ VINE_LOGE("Fail to init vine event loop");
+ vine_data_path_deinit();
+ vine_disc_deinit();
+ VINE_UNLOCK(&__vine_mutex);
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+
+ if (vine_event_loop_start()) {
+ VINE_LOGE("Fail to start vine event loop");
+ vine_event_loop_deinit();
+ vine_data_path_deinit();
+ vine_disc_deinit();
+ VINE_UNLOCK(&__vine_mutex);
+ return VINE_ERROR_OPERATION_FAILED;
+ }
+ }
+
+ __vine_ref();
+ VINE_UNLOCK(&__vine_mutex);
+ return VINE_ERROR_NONE;
+}
+
+int _vine_deinit()
+{
+ VINE_LOGD("_vine_deinit");
+ VINE_LOCK(&__vine_mutex);
+ if (!__vine_is_initialized()) {
+ VINE_LOGE("Not initialized");
+ VINE_UNLOCK(&__vine_mutex);
+ return VINE_ERROR_NOT_INITIALIZED;
+ }
+
+ if (__vine_unref() == 0) {
+ vine_event_loop_stop();
+ vine_event_loop_deinit();
+ vine_data_path_deinit();
+ vine_disc_deinit();
+ }
+ VINE_UNLOCK(&__vine_mutex);
+ return VINE_ERROR_NONE;
+}
+
+int _vine_get_capabilities(int type, int *capabilities)
+{
+ RET_VAL_IF(type < VINE_CAPA_DISCOVERY_METHODS || type > VINE_CAPA_MAX_CONNECTIONS,
+ VINE_ERROR_INVALID_PARAMETER, "Invalid type.");
+ RET_VAL_IF(capabilities == NULL, VINE_ERROR_INVALID_PARAMETER, "capabilities is NULL");
+
+ *capabilities = 0;
+ if (type == VINE_CAPA_DISCOVERY_METHODS) {
+ if (vine_disc_is_plugin_loaded(VINE_DISCOVERY_METHOD_DNS_SD))
+ *capabilities |= VINE_DISCOVERY_METHOD_DNS_SD;
+ } else if (type == VINE_CAPA_MAX_CONNECTIONS) {
+ *capabilities = VINE_DP_MAX_CONNECTIONS_NUM;
+ }
+
+ return VINE_ERROR_NONE;
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdbool.h>
+
+#include "vine-security.h"
+#include "vine-log.h"
+#include "vine-utils.h"
+
+typedef struct {
+ vine_security_type_e type;
+ int flags;
+ vine_security_tls_version_e tls_version;
+ char *ca_path;
+ char *cert_path;
+ char *key_path;
+ char *psk;
+} vine_security_s;
+
+int _vine_security_create(vine_security_h *security)
+{
+ RET_VAL_IF(security == NULL, VINE_ERROR_INVALID_PARAMETER, "security is NULL");
+
+ vine_security_s *s =
+ (vine_security_s *)calloc(1, sizeof(vine_security_s));
+ RET_VAL_IF(s == NULL, VINE_ERROR_OUT_OF_MEMORY, "Out of memory");
+
+ s->type = VINE_SECURITY_TYPE_NONE;
+ s->flags = 0;
+ s->tls_version = VINE_SECURITY_TLS_VERSION_DEFAULT;
+ s->cert_path = NULL;
+ s->key_path = NULL;
+ s->psk = NULL;
+
+ *security = s;
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_security_clone(vine_security_h *cloned, vine_security_h origin)
+{
+ RET_VAL_IF(cloned == NULL, VINE_ERROR_INVALID_PARAMETER, "cloned is NULL");
+ if (origin == NULL) {
+ VINE_LOGD("origin is NULL");
+ *cloned = NULL;
+ return VINE_ERROR_NONE;
+ }
+ RET_VAL_IF(origin == NULL, VINE_ERROR_INVALID_PARAMETER, "origin is NULL");
+
+ vine_security_s *src = (vine_security_s *)origin;
+ vine_security_s *dest =
+ (vine_security_s *)calloc(1, sizeof(vine_security_s));
+ RET_VAL_IF(dest == NULL, VINE_ERROR_OUT_OF_MEMORY, "Out of memory");
+
+ dest->type = src->type;
+ dest->flags = src->flags;
+ dest->tls_version = src->tls_version;
+ dest->ca_path = STRDUP(src->ca_path);
+ dest->cert_path = STRDUP(src->cert_path);
+ dest->key_path = STRDUP(src->key_path);
+ dest->psk = STRDUP(src->psk);
+
+ *cloned = dest;
+ return VINE_ERROR_NONE;
+}
+
+int _vine_security_destroy(vine_security_h security)
+{
+ RET_VAL_IF(security == NULL, VINE_ERROR_INVALID_PARAMETER, "security is NULL");
+
+ vine_security_s *s = (vine_security_s *)security;
+
+ free(s->cert_path);
+ free(s->key_path);
+ free(s->psk);
+ free(s);
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_security_set_type(vine_security_h security, vine_security_type_e type)
+{
+ RET_VAL_IF(security == NULL, VINE_ERROR_INVALID_PARAMETER, "security is NULL");
+ RET_VAL_IF(type < VINE_SECURITY_TYPE_NONE || type > VINE_SECURITY_TYPE_PSK_OVER_TLS,
+ VINE_ERROR_INVALID_PARAMETER, "type is invalid.");
+
+ vine_security_s *s = (vine_security_s *)security;
+ s->type = type;
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_security_get_type(vine_security_h security, vine_security_type_e *type)
+{
+ RET_VAL_IF(security == NULL, VINE_ERROR_INVALID_PARAMETER, "security is NULL");
+ RET_VAL_IF(type == NULL, VINE_ERROR_INVALID_PARAMETER, "type is NULL");
+
+ vine_security_s *s = (vine_security_s *)security;
+ *type = s->type;
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_security_set_tls_version(vine_security_h security,
+ vine_security_tls_version_e version)
+{
+ RET_VAL_IF(security == NULL, VINE_ERROR_INVALID_PARAMETER, "security is NULL");
+ RET_VAL_IF(version < VINE_SECURITY_TLS_VERSION_DEFAULT
+ || version > VINE_SECURITY_TLS_VERSION_1_3,
+ VINE_ERROR_INVALID_PARAMETER, "version is invalid.");
+
+ vine_security_s *s = (vine_security_s *)security;
+ s->tls_version = version;
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_security_get_tls_version(vine_security_h security,
+ vine_security_tls_version_e *version)
+{
+ RET_VAL_IF(security == NULL, VINE_ERROR_INVALID_PARAMETER, "security is NULL");
+ RET_VAL_IF(version == NULL, VINE_ERROR_INVALID_PARAMETER, "version is invalid.");
+
+ vine_security_s *s = (vine_security_s *)security;
+ *version = s->tls_version;
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_security_set_verification_flags(vine_security_h security, int flags)
+{
+ RET_VAL_IF(security == NULL, VINE_ERROR_INVALID_PARAMETER, "security is NULL");
+ RET_VAL_IF(flags < 0
+ || flags > (VINE_SECURITY_VERIFICATION_FLAG_ALLOW_SELF_SIGNED
+ | VINE_SECURITY_VERIFICATION_FLAG_SKIP_HOST_NAME_CHECK),
+ VINE_ERROR_INVALID_PARAMETER, "flags are invalid.");
+
+ vine_security_s *s = (vine_security_s *)security;
+ s->flags = flags;
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_security_get_verification_flags(vine_security_h security, int *flags)
+{
+ RET_VAL_IF(security == NULL, VINE_ERROR_INVALID_PARAMETER, "security is NULL");
+ RET_VAL_IF(flags == NULL, VINE_ERROR_INVALID_PARAMETER, "flags is NULL");
+
+ vine_security_s *s = (vine_security_s *)security;
+ *flags = s->flags;
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_security_set_ca_path(vine_security_h security, const char *ca_path)
+{
+ RET_VAL_IF(security == NULL, VINE_ERROR_INVALID_PARAMETER, "security is NULL");
+ RET_VAL_IF(ca_path == NULL, VINE_ERROR_INVALID_PARAMETER, "ca_path is NULL");
+
+ vine_security_s *s = (vine_security_s *)security;
+ s->ca_path = STRDUP(ca_path);
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_security_get_ca_path(vine_security_h security, char **ca_path)
+{
+ RET_VAL_IF(security == NULL, VINE_ERROR_INVALID_PARAMETER, "security is NULL");
+ RET_VAL_IF(ca_path == NULL, VINE_ERROR_INVALID_PARAMETER, "ca_path is NULL");
+
+ vine_security_s *s = (vine_security_s *)security;
+ *ca_path = STRDUP(s->ca_path);
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_security_set_cert_path(vine_security_h security, const char *cert_path)
+{
+ RET_VAL_IF(security == NULL, VINE_ERROR_INVALID_PARAMETER, "security is NULL");
+ RET_VAL_IF(cert_path == NULL, VINE_ERROR_INVALID_PARAMETER, "cert_path is NULL");
+
+ vine_security_s *s = (vine_security_s *)security;
+ s->cert_path = STRDUP(cert_path);
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_security_get_cert_path(vine_security_h security, char **cert_path)
+{
+ RET_VAL_IF(security == NULL, VINE_ERROR_INVALID_PARAMETER, "security is NULL");
+ RET_VAL_IF(cert_path == NULL, VINE_ERROR_INVALID_PARAMETER, "cert_path is NULL");
+
+ vine_security_s *s = (vine_security_s *)security;
+ *cert_path = STRDUP(s->cert_path);
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_security_set_private_key(vine_security_h security, const char *key_path)
+{
+ RET_VAL_IF(security == NULL, VINE_ERROR_INVALID_PARAMETER, "security is NULL");
+ RET_VAL_IF(key_path == NULL, VINE_ERROR_INVALID_PARAMETER, "key_path is NULL");
+
+ vine_security_s *s = (vine_security_s *)security;
+ s->key_path = STRDUP(key_path);
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_security_get_private_key(vine_security_h security, char **key_path)
+{
+ RET_VAL_IF(security == NULL, VINE_ERROR_INVALID_PARAMETER, "security is NULL");
+ RET_VAL_IF(key_path == NULL, VINE_ERROR_INVALID_PARAMETER, "key_path is NULL");
+
+ vine_security_s *s = (vine_security_s *)security;
+ *key_path = STRDUP(s->key_path);
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_security_set_psk(vine_security_h security, const char *psk)
+{
+ RET_VAL_IF(security == NULL, VINE_ERROR_INVALID_PARAMETER, "security is NULL");
+ RET_VAL_IF(psk == NULL, VINE_ERROR_INVALID_PARAMETER, "psk is NULL");
+
+ vine_security_s *s = (vine_security_s *)security;
+ s->psk = STRDUP(psk);
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_security_get_psk(vine_security_h security, char **psk)
+{
+ RET_VAL_IF(security == NULL, VINE_ERROR_INVALID_PARAMETER, "security is NULL");
+ RET_VAL_IF(psk == NULL, VINE_ERROR_INVALID_PARAMETER, "psk is NULL");
+
+ vine_security_s *s = (vine_security_s *)security;
+ *psk = STRDUP(s->psk);
+ return VINE_ERROR_NONE;
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdbool.h>
+#include <net/if.h>
+
+#include "vine.h"
+#include "vine-constants.h"
+#include "vine-disc.h"
+#include "vine-service.h"
+#include "vine-log.h"
+#include "vine-utils.h"
+
+typedef enum {
+ VINE_SERVICE_TYPE_PUBLISHED,
+ VINE_SERVICE_TYPE_DISCOVERED,
+} vine_service_type_e;
+
+typedef struct {
+ vine_service_type_e type;
+ char service_type[VINE_MAX_SERVICE_TYPE_LEN + 1];
+ char service_name[VINE_MAX_SERVICE_NAME_LEN + 1];
+ char host_name[VINE_MAX_HOST_NAME_LEN + 1];
+ char ip[VINE_MAX_IP_LEN + 1];
+ vine_address_family_e family;
+ int port;
+ map<string, string> attributes;
+ char iface_name[IF_NAMESIZE + 1];
+ vine_service_state_e state;
+
+ void *disc_handle; // Used to resolve IP address
+ vine_session_ip_resolved_cb ip_resolved_cb;
+ void *ip_resolved_cb_data;
+} vine_service_s;
+
+bool _vine_service_check_service_type(const char *service_type)
+{
+ RET_VAL_IF(service_type == NULL, false, "service_type is NULL");
+ int len = strlen(service_type);
+ return len > 0 && len <= VINE_MAX_SERVICE_TYPE_LEN;
+}
+
+static bool _vine_service_check_service_name(const char *service_name)
+{
+ RET_VAL_IF(service_name == NULL, false, "service_name is NULL");
+ int len = strlen(service_name);
+ return len > 0 && len <= VINE_MAX_SERVICE_NAME_LEN;
+}
+
+bool _is_discovered_service(vine_service_h service)
+{
+ vine_service_s *s = (vine_service_s *)service;
+ return s->type == VINE_SERVICE_TYPE_DISCOVERED;
+}
+
+int _vine_service_create(vine_service_h *service, bool published)
+{
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+
+ vine_service_s *s = new vine_service_s;
+ memset(s->service_type, 0, VINE_MAX_SERVICE_TYPE_LEN + 1);
+ memset(s->service_name, 0, VINE_MAX_SERVICE_NAME_LEN + 1);
+ memset(s->host_name, 0, VINE_MAX_HOST_NAME_LEN + 1);
+ memset(s->iface_name, 0, IF_NAMESIZE + 1);
+ s->port = -1;
+ *service = s;
+ s->type = published ? VINE_SERVICE_TYPE_PUBLISHED : VINE_SERVICE_TYPE_DISCOVERED;
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_service_destroy(vine_service_h service)
+{
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+ vine_service_s *s = (vine_service_s *)service;
+
+ s->attributes.clear();
+ delete s;
+ return VINE_ERROR_NONE;
+}
+
+int _vine_service_clone(vine_service_h origin, vine_service_h *cloned)
+{
+ RET_VAL_IF(origin == NULL, VINE_ERROR_INVALID_PARAMETER, "origin is NULL");
+ RET_VAL_IF(cloned == NULL, VINE_ERROR_INVALID_PARAMETER, "cloned is NULL");
+
+ vine_service_s *origin_service = (vine_service_s *)origin;
+ vine_service_s *cloned_service = new vine_service_s;
+
+ memcpy(cloned_service, origin_service, sizeof(vine_service_s));
+ cloned_service->attributes = origin_service->attributes;
+
+ *cloned = cloned_service;
+ VINE_LOGD("Cloned service[%p]", cloned_service);
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_service_set_type(
+ vine_service_h service, const char *service_type)
+{
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+ RET_VAL_IF(_vine_service_check_service_type(service_type) == false,
+ VINE_ERROR_INVALID_PARAMETER, "invalid length of service_type");
+
+ vine_service_s *s = (vine_service_s *)service;
+ strncpy(s->service_type, service_type, VINE_MAX_SERVICE_TYPE_LEN);
+
+ return VINE_ERROR_NONE;
+}
+
+const char *_vine_service_get_type(vine_service_h service)
+{
+ RET_VAL_IF(service == NULL, NULL, "service is NULL");
+
+ vine_service_s *s = (vine_service_s *)service;
+ return s->service_type;
+}
+
+int _vine_service_set_name(
+ vine_service_h service, const char *service_name)
+{
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+ RET_VAL_IF(_vine_service_check_service_name(service_name) == false,
+ VINE_ERROR_INVALID_PARAMETER, "invalid length of service_name");
+
+ vine_service_s *s = (vine_service_s *)service;
+ strncpy(s->service_name, service_name, VINE_MAX_SERVICE_TYPE_LEN);
+
+ return VINE_ERROR_NONE;
+}
+
+const char *_vine_service_get_name(vine_service_h service)
+{
+ RET_VAL_IF(service == NULL, NULL, "service is NULL");
+
+ vine_service_s *s = (vine_service_s *)service;
+ return s->service_name;
+}
+
+int _vine_service_set_host_name(
+ vine_service_h service, const char *host_name)
+{
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+
+ vine_service_s *s = (vine_service_s *)service;
+ strncpy(s->host_name, host_name, VINE_MAX_HOST_NAME_LEN);
+
+ return VINE_ERROR_NONE;
+}
+
+const char *_vine_service_get_host_name(vine_service_h service)
+{
+ RET_VAL_IF(service == NULL, NULL, "service is NULL");
+
+ vine_service_s *s = (vine_service_s *)service;
+ return s->host_name;
+}
+
+static bool __check_len_key(const char *key)
+{
+ RET_VAL_IF(key == NULL, false, "key is NULL");
+ int len = strlen(key);
+ return len > 0 && len <= VINE_MAX_KEY_LEN;
+}
+
+int _vine_service_add_attribute(vine_service_h service,
+ const char *key, const char *value)
+{
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+ RET_VAL_IF(__check_len_key(key) == false,
+ VINE_ERROR_INVALID_PARAMETER, "invalid length of key");
+
+ VINE_LOGD("Key[%s] value[%s]", key, value);
+
+ vine_service_s *s = (vine_service_s *)service;
+
+ if (s->attributes.find(key) != s->attributes.end()) {
+ VINE_LOGE("Key[%s] already exits", key);
+ return VINE_ERROR_INVALID_OPERATION;
+ }
+
+ // value can be NULL, which represents an empty string.
+ s->attributes[key] = value ? value : "";
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_service_remove_attribute(vine_service_h service, const char *key)
+{
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+ RET_VAL_IF(key == NULL, VINE_ERROR_INVALID_PARAMETER, "key is NULL");
+
+ vine_service_s *s = (vine_service_s *)service;
+
+ string attr_key(key);
+ if (s->attributes.find(attr_key) == s->attributes.end()) {
+ VINE_LOGE("No key[%s]", key);
+ return VINE_ERROR_INVALID_PARAMETER;
+ }
+
+ s->attributes.erase(attr_key);
+ return VINE_ERROR_NONE;
+}
+
+map<string, string> _vine_service_get_attributes(vine_service_h service)
+{
+ if (service == NULL) {
+ VINE_LOGE("service is NULL");
+ return map<string, string>();
+ }
+
+ vine_service_s *s = (vine_service_s *)service;
+ return s->attributes;
+}
+
+int _vine_service_foreach_attribute(vine_service_h service,
+ vine_service_attribute_cb callback, void *user_data)
+{
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+ RET_VAL_IF(callback == NULL, VINE_ERROR_INVALID_PARAMETER, "callback is NULL");
+
+ vine_service_s *s = (vine_service_s *)service;
+
+ for (const auto &kv : s->attributes) {
+ if (!callback(kv.first.c_str(), kv.second.c_str(), user_data))
+ break;
+ }
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_service_set_iface_name(vine_service_h service, const char *iface_name)
+{
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+
+ vine_service_s *s = (vine_service_s *)service;
+ strncpy(s->iface_name, iface_name, IF_NAMESIZE);
+
+ return VINE_ERROR_NONE;
+}
+
+const char *_vine_service_get_iface_name(vine_service_h service)
+{
+ RET_VAL_IF(service == NULL, NULL, "service is NULL");
+
+ vine_service_s *s = (vine_service_s *)service;
+ return s->iface_name;
+}
+
+int _vine_service_get_disc_handle(vine_service_h service,
+ vine_discovery_method_e disc_method, void **disc_handle)
+{
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+
+ vine_service_s *s = (vine_service_s *)service;
+ if (s->disc_handle == NULL) {
+ int ret = vine_disc_create(disc_method, &s->disc_handle);
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to vine_disc_create");
+ }
+ *disc_handle = s->disc_handle;
+ return VINE_ERROR_NONE;
+}
+
+int _vine_service_set_ip_resolved_cb(vine_service_h service,
+ vine_session_ip_resolved_cb callback, void *user_data)
+{
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+ RET_VAL_IF(callback == NULL, VINE_ERROR_INVALID_PARAMETER, "callback is NULL");
+
+ RET_VAL_IF(!_is_discovered_service(service), VINE_ERROR_INVALID_OPERATION,
+ "only for discovered service");
+
+ vine_service_s *s = (vine_service_s *)service;
+ s->ip_resolved_cb = callback;
+ s->ip_resolved_cb_data = user_data;
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_service_unset_ip_resolved_cb(vine_service_h service)
+{
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+
+ RET_VAL_IF(!_is_discovered_service(service), VINE_ERROR_INVALID_OPERATION,
+ "only for discovered service");
+
+ vine_service_s *s = (vine_service_s *)service;
+ s->ip_resolved_cb = NULL;
+ s->ip_resolved_cb_data = NULL;
+
+ return VINE_ERROR_NONE;
+}
+
+vine_session_ip_resolved_cb _vine_service_ip_resolved_cb(vine_service_h service)
+{
+ RET_VAL_IF(service == NULL, NULL, "service is NULL");
+
+ vine_service_s *s = (vine_service_s *)service;
+ return s->ip_resolved_cb;
+}
+
+void * _vine_service_ip_resolved_cb_data(vine_service_h service)
+{
+ RET_VAL_IF(service == NULL, NULL, "service is NULL");
+
+ vine_service_s *s = (vine_service_s *)service;
+ return s->ip_resolved_cb_data;
+}
+
+static bool __check_port_number(int port)
+{
+ return port >= 0 && port <= 65535;
+}
+
+int _vine_service_set_port(vine_service_h service, int port)
+{
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+ RET_VAL_IF(__check_port_number(port) == false,
+ VINE_ERROR_INVALID_PARAMETER, "A port number ranges from 0 to 65535");
+
+ vine_service_s *s = (vine_service_s *)service;
+ s->port = port;
+ return VINE_ERROR_NONE;
+}
+
+int _vine_service_get_port(vine_service_h service)
+{
+ RET_VAL_IF(service == NULL, 0, "service is NULL");
+
+ vine_service_s *s = (vine_service_s *)service;
+ return s->port;
+}
+
+int _vine_service_set_state(vine_service_h service, vine_service_state_e state)
+{
+ RET_VAL_IF(service == NULL, 0, "service is NULL");
+ vine_service_s *s = (vine_service_s *)service;
+ s->state = state;
+ return VINE_ERROR_NONE;
+}
+
+vine_service_state_e _vine_service_get_state(vine_service_h service)
+{
+ RET_VAL_IF(service == NULL, VINE_SERVICE_UNAVAILABLE, "service is NULL");
+ vine_service_s *s = (vine_service_s *)service;
+ return s->state;
+}
+
+int _vine_service_set_disc_handle(vine_service_h service, void *disc_handle)
+{
+ RET_VAL_IF(service == NULL, 0, "service is NULL");
+ vine_service_s *s = (vine_service_s *)service;
+ s->disc_handle = disc_handle;
+ return VINE_ERROR_NONE;
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <map>
+
+#include "vine.h"
+#include "vine-disc.h"
+#include "vine-event-loop.h"
+#include "vine-session.h"
+#include "vine-service.h"
+#include "vine-log.h"
+#include "vine-utils.h"
+
+using namespace std;
+
+typedef struct {
+ vine_event_queue_h event_fd;
+
+ vine_discovery_method_e disc_method;
+ vine_disc_h disc_handle;
+
+ vine_session_registered_cb registered_cb;
+ void *registered_cb_data;
+ vine_session_discovered_cb discovered_cb;
+ void *discovered_cb_data;
+
+ bool is_running;
+ bool registered;
+ bool discovered;
+} vine_session_s;
+
+static void __vine_session_set_is_running(vine_session_h session, bool is_running)
+{
+ vine_session_s *s = (vine_session_s *)session;
+ s->is_running = is_running;
+}
+
+static bool __vine_session_is_running(vine_session_h session)
+{
+ vine_session_s *s = (vine_session_s *)session;
+ return s->is_running;
+}
+
+static bool __vine_session_is_registered(vine_session_h session)
+{
+ vine_session_s *s = (vine_session_s *)session;
+ return s->registered;
+}
+
+static bool __vine_session_is_discovered(vine_session_h session)
+{
+ vine_session_s *s = (vine_session_s *)session;
+ return s->discovered;
+}
+
+static void __vine_session_set_registered(vine_session_h session, bool registered)
+{
+ vine_session_s *s = (vine_session_s *)session;
+ s->registered = registered;
+}
+
+static void __vine_session_set_discovered(vine_session_h session, bool discovered)
+{
+ vine_session_s *s = (vine_session_s *)session;
+ s->discovered = discovered;
+}
+
+int _vine_session_create(vine_session_h *session)
+{
+ RET_VAL_IF(session == NULL, VINE_ERROR_INVALID_PARAMETER, "session is NULL");
+
+ vine_session_s *s =
+ (vine_session_s *)calloc(1, sizeof(vine_session_s));
+ RET_VAL_IF(s == NULL, VINE_ERROR_OUT_OF_MEMORY, "Out of memory");
+
+ VINE_LOGD("New Session. session[%p]", s);
+ *session = s;
+ s->is_running = false;
+ s->registered = false;
+ s->discovered = false;
+ s->disc_handle = NULL;
+
+ int ret = vine_event_queue_create(&(s->event_fd));
+ if (ret != VINE_ERROR_NONE) {
+ VINE_LOGE("Fail to create eventfd %d", ret);
+ free(*session);
+ return ret;
+ }
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_session_destroy(vine_session_h session)
+{
+ RET_VAL_IF(session == NULL, VINE_ERROR_INVALID_PARAMETER, "session is NULL");
+ vine_session_s *s = (vine_session_s *)session;
+
+ VINE_LOGD("Destroy Session. session[%p]", session);
+ vine_event_queue_destroy(s->event_fd);
+ vine_disc_destroy(s->disc_handle);
+ s->disc_handle = NULL;
+ free(session);
+ return VINE_ERROR_NONE;
+}
+
+int _vine_session_set_registered_cb(vine_session_h session,
+ vine_session_registered_cb callback, void *user_data)
+{
+ RET_VAL_IF(session == NULL, VINE_ERROR_INVALID_PARAMETER, "session is NULL");
+ RET_VAL_IF(callback == NULL, VINE_ERROR_INVALID_PARAMETER, "callback is NULL");
+
+ VINE_LOGD("Set registered callback. session[%p]", session);
+ vine_session_s *s = (vine_session_s *)session;
+ s->registered_cb = callback;
+ s->registered_cb_data = user_data;
+ return VINE_ERROR_NONE;
+}
+
+int _vine_session_unset_registered_cb(vine_session_h session)
+{
+ RET_VAL_IF(session == NULL, VINE_ERROR_INVALID_PARAMETER, "session is NULL");
+
+ VINE_LOGD("Unset registered callback. session[%p]", session);
+ vine_session_s *s = (vine_session_s *)session;
+ s->registered_cb = NULL;
+ s->registered_cb_data = NULL;
+ return VINE_ERROR_NONE;
+}
+
+int _vine_session_set_discovered_cb(vine_session_h session,
+ vine_session_discovered_cb callback, void *user_data)
+{
+ RET_VAL_IF(session == NULL, VINE_ERROR_INVALID_PARAMETER, "session is NULL");
+ RET_VAL_IF(callback == NULL, VINE_ERROR_INVALID_PARAMETER, "callback is NULL");
+
+ VINE_LOGD("Set discovered callback. session[%p]", session);
+ vine_session_s *s = (vine_session_s *)session;
+ s->discovered_cb = callback;
+ s->discovered_cb_data = user_data;
+ return VINE_ERROR_NONE;
+}
+
+int _vine_session_unset_discovered_cb(vine_session_h session)
+{
+ RET_VAL_IF(session == NULL, VINE_ERROR_INVALID_PARAMETER, "session is NULL");
+
+ VINE_LOGD("Unset discovered callback. session[%p]", session);
+ vine_session_s *s = (vine_session_s *)session;
+ s->discovered_cb = NULL;
+ s->discovered_cb_data = NULL;
+ return VINE_ERROR_NONE;
+}
+
+static bool __check_disc_method(vine_discovery_method_e method)
+{
+ return method == VINE_DISCOVERY_METHOD_DNS_SD;
+}
+
+int _vine_session_set_discovery_method(vine_session_h session, vine_discovery_method_e method)
+{
+ RET_VAL_IF(session == NULL, VINE_ERROR_INVALID_PARAMETER, "session is NULL");
+ RET_VAL_IF(__check_disc_method(method) == false, VINE_ERROR_INVALID_PARAMETER,
+ "invalid service discovery method");
+
+ VINE_LOGD("Set service discovery method. session[%p] method[%d]", session, method);
+ vine_session_s *s = (vine_session_s *)session;
+ s->disc_method = method;
+
+ return VINE_ERROR_NONE;
+}
+
+static void __published_cb(vine_disc_h disc,
+ const char *service_name, vine_error_e error, void *user_data)
+{
+ VINE_LOGD("Service is published");
+
+ vine_session_s *s = (vine_session_s *)user_data;
+ VINE_LOGD("session[%p] service_name[%s], error[%d]", s, service_name, error);
+
+ if (error != VINE_ERROR_NONE && error != VINE_ERROR_NAME_CONFLICT) {
+ VINE_LOGE("Fail to publish");
+ __vine_session_set_is_running(s, false);
+ } else {
+ __vine_session_set_registered(s, true);
+ }
+
+ if (s->registered_cb)
+ s->registered_cb(s, service_name, error, s->registered_cb_data);
+}
+
+int _vine_session_register(vine_session_h session,
+ vine_service_h service, const char *iface_name)
+{
+ RET_VAL_IF(session == NULL, VINE_ERROR_INVALID_PARAMETER, "session is NULL");
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+ RET_VAL_IF(__vine_session_is_running(session), VINE_ERROR_INVALID_OPERATION,
+ "session is already running");
+
+ VINE_LOGD("Register a service. session[%p]", session);
+
+ int ret = VINE_ERROR_NONE;
+ vine_session_s *s = (vine_session_s *)session;
+
+ if (s->disc_handle == NULL) {
+ ret = vine_disc_create(s->disc_method, &s->disc_handle);
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to vine_disc_create");
+ }
+
+ ret = vine_disc_publish(s->disc_handle, service, iface_name,
+ __published_cb, session,
+ s->event_fd);
+ if (ret != VINE_ERROR_NONE) {
+ VINE_LOGE("Fail to vine_disc_publish");
+ vine_disc_destroy(s->disc_handle);
+ s->disc_handle = NULL;
+ return ret;
+ }
+
+ __vine_session_set_is_running(session, true);
+ return VINE_ERROR_NONE;
+}
+
+int _vine_session_unregister(vine_session_h session)
+{
+ RET_VAL_IF(session == NULL, VINE_ERROR_INVALID_PARAMETER, "session is NULL");
+ RET_VAL_IF(!__vine_session_is_running(session), VINE_ERROR_INVALID_OPERATION,
+ "session is not running");
+ RET_VAL_IF(!__vine_session_is_registered(session), VINE_ERROR_INVALID_OPERATION,
+ "session is not registered");
+
+ int ret = VINE_ERROR_NONE;
+ vine_session_s *s = (vine_session_s *)session;
+
+ ret = vine_disc_stop_publish(s->disc_handle);
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to vine_disc_stop_publish");
+
+ __vine_session_set_is_running(session, false);
+ __vine_session_set_registered(session, false);
+
+ return VINE_ERROR_NONE;
+}
+
+static int __vine_set_discovered_service(vine_service_h service,
+ const char *service_type, const char *service_name,
+ const char *host_name, int port, const map<string, string> &attr,
+ const char *iface_name)
+{
+ int ret = VINE_ERROR_NONE;
+ ret = _vine_service_set_type(service, service_type);
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to set service type");
+ ret = _vine_service_set_name(service, service_name);
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to set service name");
+ ret = _vine_service_set_host_name(service, host_name);
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to set host name");
+ ret = _vine_service_set_port(service, port);
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to set port");
+
+ for (const auto &kv : attr)
+ _vine_service_add_attribute(service, kv.first.c_str(), kv.second.c_str());
+
+ ret = _vine_service_set_iface_name(service, iface_name);
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to set iface_name");
+
+ return VINE_ERROR_NONE;
+}
+
+static void __discovered_cb(vine_disc_h disc, bool available,
+ const char *service_type, const char *service_name,
+ const char *host_name, int port, const map<string, string> &attr,
+ const char *iface_name, int more_coming, void *user_data)
+{
+ VINE_LOGD("Service is discovered. Available[%d]", available);
+ int ret = VINE_ERROR_NONE;
+
+ vine_session_s *s = (vine_session_s *)user_data;
+ VINE_LOGD("session[%p] service_name[%s], host_name[%s], port[%d], iface[%s]",
+ s, service_name, host_name, port, iface_name);
+
+ vine_service_h discovered_service;
+ ret = _vine_service_create(&discovered_service, false);
+ RET_IF(ret != VINE_ERROR_NONE, "Fail to create a service");
+
+ ret = _vine_service_set_disc_handle(discovered_service, disc);
+ RET_IF(ret != VINE_ERROR_NONE, "Fail to set disc_handle");
+
+ ret = __vine_set_discovered_service(discovered_service,
+ service_type, service_name, host_name, port, attr, iface_name);
+ if (ret != VINE_ERROR_NONE) {
+ VINE_LOGE("Fail to set a service. error(%d)", ret);
+ _vine_service_destroy(discovered_service);
+ return;
+ }
+
+ vine_service_state_e state = available ? VINE_SERVICE_AVAILABLE
+ : VINE_SERVICE_UNAVAILABLE;
+ _vine_service_set_state(discovered_service, state);
+
+ if (s->discovered_cb)
+ s->discovered_cb(s, discovered_service, state, s->discovered_cb_data);
+}
+
+int _vine_session_start_discovery(vine_session_h session,
+ const char *service_type, const char *iface_name)
+{
+ RET_VAL_IF(session == NULL, VINE_ERROR_INVALID_PARAMETER, "session is NULL");
+ RET_VAL_IF(_vine_service_check_service_type(service_type) == false,
+ VINE_ERROR_INVALID_PARAMETER, "invalid length of service_type");
+ RET_VAL_IF(__vine_session_is_running(session), VINE_ERROR_INVALID_OPERATION,
+ "session is already running");
+
+ VINE_LOGD("Subscribe a service. session[%p] service_type[%s]", session, service_type);
+ int ret = VINE_ERROR_NONE;
+ vine_session_s *s = (vine_session_s *)session;
+
+ if (s->disc_handle == NULL) {
+ ret = vine_disc_create(s->disc_method, &s->disc_handle);
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to vine_disc_create");
+ }
+
+ ret = vine_disc_subscribe(s->disc_handle, service_type, iface_name,
+ __discovered_cb, session,
+ s->event_fd);
+ if (ret != VINE_ERROR_NONE) {
+ VINE_LOGE("Fail to vine_disc_subscribe");
+ vine_disc_destroy(s->disc_handle);
+ s->disc_handle = NULL;
+ return ret;
+ }
+
+ __vine_session_set_is_running(session, true);
+ __vine_session_set_discovered(s, true);
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_session_stop_discovery(vine_session_h session)
+{
+ RET_VAL_IF(session == NULL, VINE_ERROR_INVALID_PARAMETER, "session is NULL");
+ RET_VAL_IF(!__vine_session_is_running(session), VINE_ERROR_INVALID_OPERATION,
+ "session is not running");
+ RET_VAL_IF(!__vine_session_is_discovered(session), VINE_ERROR_INVALID_OPERATION,
+ "session doesn't start discovery");
+
+ int ret = VINE_ERROR_NONE;
+ vine_session_s *s = (vine_session_s *)session;
+
+ ret = vine_disc_stop_subscribe(s->disc_handle);
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to vine_disc_stop_subscribe");
+
+ __vine_session_set_is_running(session, false);
+ __vine_session_set_discovered(session, false);
+
+ return VINE_ERROR_NONE;
+}
+
+static void __ip_resolved_cb(vine_disc_h disc, vine_service_h service, bool add,
+ const char *ip, vine_address_family_e address_family, void *user_data)
+{
+ VINE_LOGD("IP is resolved");
+
+ vine_session_s *s = (vine_session_s *)user_data;
+ VINE_LOGD("session[%p] service[%p]", s, service);
+ VINE_LOGD("IP[%s], address family[%d] %s", ip, address_family,
+ add ? "Add" : "Remove");
+
+ vine_session_ip_resolved_cb cb = _vine_service_ip_resolved_cb(service);
+ void *cb_data = _vine_service_ip_resolved_cb_data(service);
+ if (cb && add)
+ cb(s, service, ip, address_family, cb_data);
+}
+
+int _vine_session_set_ip_resolved_cb(vine_session_h session,
+ vine_service_h service, vine_session_ip_resolved_cb callback, void *user_data)
+{
+ RET_VAL_IF(session == NULL, VINE_ERROR_INVALID_PARAMETER, "session is NULL");
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+
+ VINE_LOGD("Resolve IP address for a service[%p]. session[%p]", service, session);
+
+ int ret = VINE_ERROR_NONE;
+ vine_session_s *s = (vine_session_s *)session;
+
+ vine_disc_h disc_handle;
+ ret = _vine_service_get_disc_handle(service, VINE_DISCOVERY_METHOD_DNS_SD, &disc_handle);
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to _vine_service_get_disc_handle");
+
+ _vine_service_set_ip_resolved_cb(service, callback, user_data);
+
+ ret = vine_disc_resolve_ip(disc_handle, service,
+ __ip_resolved_cb, session,
+ s->event_fd);
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to vine_disc_resolve_ip");
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_session_unset_ip_resolved_cb(vine_session_h session,
+ vine_service_h service)
+{
+ RET_VAL_IF(session == NULL, VINE_ERROR_INVALID_PARAMETER, "session is NULL");
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+
+ VINE_LOGD("Cancel resolving IP address for a service[%p]. session[%p]", service, session);
+
+ int ret = VINE_ERROR_NONE;
+ vine_disc_h disc_handle = NULL;
+ ret = _vine_service_get_disc_handle(service, VINE_DISCOVERY_METHOD_DNS_SD, &disc_handle);
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to _vine_service_get_disc_handle");
+
+ _vine_service_set_ip_resolved_cb(service, NULL, NULL);
+
+ ret = vine_disc_cancel_resolve_ip(disc_handle, service);
+ RET_VAL_IF(ret != VINE_ERROR_NONE, ret, "Fail to vine_disc_cancel_resolve_ip");
+
+ return VINE_ERROR_NONE;
+}
+
+int _vine_session_get_event_queue(vine_session_h session, vine_event_queue_h *eq)
+{
+ RET_VAL_IF(session == NULL, VINE_ERROR_INVALID_PARAMETER, "session is NULL");
+
+ vine_session_s *s = (vine_session_s *)session;
+ *eq = s->event_fd;
+ return VINE_ERROR_NONE;
+}
+
+int _vine_session_get_event_fd(vine_session_h session, int *fd)
+{
+ RET_VAL_IF(session == NULL, VINE_ERROR_INVALID_PARAMETER, "session is NULL");
+
+ vine_session_s *s = (vine_session_s *)session;
+ vine_event_loop_get_eventfd(s->event_fd, fd);
+ return VINE_ERROR_NONE;
+}
+
+int _vine_session_process_event(vine_session_h session)
+{
+ RET_VAL_IF(session == NULL, VINE_ERROR_INVALID_PARAMETER, "session is NULL");
+
+ VINE_LOGD("Process event. session[%p]", session);
+ vine_session_s *s = (vine_session_s *)session;
+ return vine_event_loop_process(s->event_fd);
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+
+#include <sys/timerfd.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "vine-event-loop.h"
+#include "vine-log.h"
+#include "vine-timer.h"
+
+VINE_NAMESPACE_USE;
+
+#define TIMER_SIGNAL_NO SIGRTMIN
+VineTimer::VineTimer()
+ : __data(NULL), __timer_fd(-1)
+{
+ VINE_LOGD("Timer is created [%p]", this);
+}
+
+
+VineTimer::~VineTimer()
+{
+ stop();
+}
+
+void VineTimer::start(unsigned int timeout_ms,
+ std::function<void(void *user_data)> callback, void *user_data)
+{
+ __timer_fd = timerfd_create(CLOCK_MONOTONIC, 0);
+ if (__timer_fd == -1) {
+ VINE_LOGE("timerfd_create fails %d", errno);
+ return;
+ }
+
+ __timer_cb = callback;
+ __data = user_data;
+
+ struct itimerspec value;
+ value.it_interval.tv_sec = 0;
+ value.it_interval.tv_nsec = 0;
+ value.it_value.tv_sec = timeout_ms / 1000;
+ value.it_value.tv_nsec = (timeout_ms % 1000) * 1000000;
+ if (timerfd_settime(__timer_fd, 0, &value, NULL) != 0) {
+ VINE_LOGE("timerfd_settime fails %d", errno);
+ close(__timer_fd);
+ return;
+ }
+
+ vine_event_loop_add_io_handler(__timer_fd, VINE_POLLIN, __timer_handler, this);
+ VINE_LOGI("Start VineTimer[%p] timer_fd[%d]", this, __timer_fd);
+}
+
+void VineTimer::stop()
+{
+ VINE_LOGI("Stop VineTimer[%p] timer_fd[%d]", this, __timer_fd);
+ if (__timer_fd != -1) {
+ vine_event_loop_del_io_handler(__timer_fd);
+ close(__timer_fd);
+ __timer_fd = -1;
+ }
+}
+
+void VineTimer::expired()
+{
+ VINE_LOGI("VineTimer[%p] is expired. timer_fd[%d]", this, __timer_fd);
+ close(__timer_fd);
+ __timer_fd = -1;
+ __timer_cb(__data);
+}
+
+bool VineTimer::is_running()
+{
+ return __timer_fd >= 0;
+}
+
+void VineTimer::__timer_handler(int fd, int events, void *user_data)
+{
+ VINE_LOGD("+");
+
+ VineTimer *timer = (VineTimer *)user_data;
+ timer->expired();
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <string.h>
+
+#include "vine-utils.h"
+
+void vine_mutex_lock(pthread_mutex_t *mutex, const char *func, int line)
+{
+ int ret = pthread_mutex_lock(mutex);
+ if (ret != 0)
+ VINE_LOGE("%s:%d error %d", func, line, ret);
+}
+
+void vine_mutex_unlock(pthread_mutex_t *mutex, const char *func, int line)
+{
+ int ret = pthread_mutex_unlock(mutex);
+ if (ret != 0)
+ VINE_LOGE("%s:%d error %d", func, line, ret);
+}
+
+char *strndup(const char *s, size_t n) {
+ char *p;
+ size_t n1;
+
+ for (n1 = 0; n1 < n && s[n1] != '\0'; n1++)
+ continue;
+ p = (char *)malloc(n + 1);
+ if (p != NULL) {
+ memcpy(p, s, n1);
+ p[n1] = '\0';
+ }
+ return p;
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "vine.h"
+
+#include "vine-constants.h"
+#include "vine-dp.h"
+#include "vine-data-path.h"
+#include "vine-event-loop.h"
+#include "vine-log.h"
+#include "vine-private.h"
+#include "vine-security.h"
+#include "vine-service.h"
+#include "vine-session.h"
+#include "vine-utils.h"
+
+API int vine_initialize()
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_init();
+}
+
+API int vine_deinitialize()
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_deinit();
+}
+
+API int vine_get_capabilities(vine_capability_type_e type, int *capabilities)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_get_capabilities(type, capabilities);
+}
+
+API int vine_session_get_event_fd(vine_session_h session, int *fd)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_session_get_event_fd(session, fd);
+}
+
+API int vine_session_process_event(vine_session_h session)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_session_process_event(session);
+}
+
+API int vine_session_create(vine_session_h *session)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_session_create(session);
+}
+
+API int vine_session_destroy(vine_session_h session)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_session_destroy(session);
+}
+
+API int vine_session_set_registered_cb(vine_session_h session,
+ vine_session_registered_cb callback, void *user_data)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_session_set_registered_cb(session, callback, user_data);
+}
+
+API int vine_session_unset_registered_cb(vine_session_h session)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_session_unset_registered_cb(session);
+}
+
+API int vine_session_set_discovered_cb(vine_session_h session,
+ vine_session_discovered_cb callback, void *user_data)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_session_set_discovered_cb(session, callback, user_data);
+}
+
+API int vine_session_unset_discovered_cb(vine_session_h session)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_session_unset_discovered_cb(session);
+}
+
+API int vine_session_set_discovery_method(vine_session_h session, vine_discovery_method_e method)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_session_set_discovery_method(session, method);
+}
+
+API int vine_session_register(vine_session_h session,
+ vine_service_h service, const char *iface_name)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_session_register(session, service, iface_name);
+}
+
+API int vine_session_unregister(vine_session_h session)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_session_unregister(session);
+}
+
+API int vine_session_start_discovery(vine_session_h session,
+ const char *service_type, const char *iface_name)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_session_start_discovery(session, service_type, iface_name);
+}
+
+API int vine_session_stop_discovery(vine_session_h session)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_session_stop_discovery(session);
+}
+
+API int vine_session_set_ip_resolved_cb(vine_session_h session,
+ vine_service_h service, vine_session_ip_resolved_cb callback, void *user_data)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_session_set_ip_resolved_cb(session, service, callback, user_data);
+}
+
+API int vine_session_unset_ip_resolved_cb(vine_session_h session,
+ vine_service_h service)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_session_unset_ip_resolved_cb(session, service);
+}
+
+API int vine_service_create(vine_service_h *service)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_service_create(service, true);
+}
+
+API int vine_service_destroy(vine_service_h service)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_service_destroy(service);
+}
+
+API int vine_service_clone(vine_service_h origin, vine_service_h *cloned)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_service_clone(origin, cloned);
+}
+
+API int vine_service_set_type(vine_service_h service, const char *service_type)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+ RET_VAL_IF(_is_discovered_service(service), VINE_ERROR_INVALID_OPERATION,
+ "cannot set service type for a discovered service");
+ return _vine_service_set_type(service, service_type);
+}
+
+API int vine_service_get_type(vine_service_h service, char **service_type)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+ RET_VAL_IF(service_type == NULL, VINE_ERROR_INVALID_PARAMETER,
+ "service_type is NULL");
+
+ *service_type = (char *)strndup(_vine_service_get_type(service),
+ VINE_MAX_SERVICE_TYPE_LEN);
+ RET_VAL_IF(*service_type == NULL, VINE_ERROR_OUT_OF_MEMORY, "Out of memory");
+ return VINE_ERROR_NONE;
+}
+
+API int vine_service_set_name(vine_service_h service, const char *service_name)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+ RET_VAL_IF(_is_discovered_service(service), VINE_ERROR_INVALID_OPERATION,
+ "cannot set service name for a discovered service");
+ return _vine_service_set_name(service, service_name);
+}
+
+API int vine_service_get_name(vine_service_h service, char **service_name)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+ RET_VAL_IF(service_name == NULL, VINE_ERROR_INVALID_PARAMETER,
+ "service_name is NULL");
+
+ *service_name = (char *)strndup(_vine_service_get_name(service),
+ VINE_MAX_SERVICE_TYPE_LEN);
+ RET_VAL_IF(*service_name == NULL, VINE_ERROR_OUT_OF_MEMORY, "Out of memory");
+ return VINE_ERROR_NONE;
+}
+
+API int vine_service_add_attribute(vine_service_h service,
+ const char *key, const char *value)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+ RET_VAL_IF(_is_discovered_service(service), VINE_ERROR_INVALID_OPERATION,
+ "cannot add attributes for a discovered service");
+ return _vine_service_add_attribute(service, key, value);
+}
+
+API int vine_service_remove_attribute(vine_service_h service, const char *key)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+ RET_VAL_IF(_is_discovered_service(service), VINE_ERROR_INVALID_OPERATION,
+ "cannot add attributes for a discovered service");
+ return _vine_service_remove_attribute(service, key);
+}
+
+API int vine_service_foreach_attribute(vine_service_h service,
+ vine_service_attribute_cb callback, void *user_data)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_service_foreach_attribute(service, callback, user_data);
+}
+
+API int vine_service_set_port(vine_service_h service, int port)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+ RET_VAL_IF(_is_discovered_service(service), VINE_ERROR_INVALID_OPERATION,
+ "cannot set port for a discovered service");
+ return _vine_service_set_port(service, port);
+}
+
+API int vine_service_get_port(vine_service_h service, int *port)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ RET_VAL_IF(service == NULL, VINE_ERROR_INVALID_PARAMETER, "service is NULL");
+ RET_VAL_IF(port == NULL, VINE_ERROR_INVALID_PARAMETER, "port is NULL");
+
+ *port = _vine_service_get_port(service);
+ return VINE_ERROR_NONE;
+}
+
+API int vine_dp_create(vine_session_h session, vine_dp_type_e type, vine_dp_h *dp)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_dp_create(session, type, dp);
+}
+
+API int vine_dp_destroy(vine_dp_h dp)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_dp_destroy(dp);
+}
+
+API int vine_dp_set_iface_name(vine_dp_h dp, const char *iface_name)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_dp_set_iface_name(dp, iface_name);
+}
+
+API int vine_dp_set_address_family(vine_dp_h dp, vine_address_family_e addr_family)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_dp_set_addr_family(dp, addr_family);
+}
+
+API int vine_dp_set_remote_ip(vine_dp_h dp, vine_address_family_e addr_family, const char *ip)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_dp_set_remote_ip(dp, addr_family, ip);
+}
+
+API int vine_dp_set_port(vine_dp_h dp, int port)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_dp_set_port(dp, port);
+}
+
+API int vine_dp_get_port(vine_dp_h dp, int *port)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_dp_get_port(dp, port);
+}
+
+API int vine_dp_set_topic(vine_dp_h dp, const char *topic)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_dp_set_topic(dp, topic);
+}
+
+API int vine_dp_set_max_connections(vine_dp_h dp, int max_conn)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_dp_set_max_connections(dp, max_conn);
+}
+
+API int vine_dp_set_security(vine_dp_h dp, vine_security_h security)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_dp_set_security(dp, security);
+}
+
+API int vine_dp_set_accepted_cb(vine_dp_h dp, vine_dp_accepted_cb callback, void *user_data)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_dp_set_accepted_cb(dp, callback, user_data);
+}
+
+API int vine_dp_unset_accepted_cb(vine_dp_h dp)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_dp_unset_accepted_cb(dp);
+}
+
+API int vine_dp_set_terminated_cb(vine_dp_h dp, vine_dp_terminated_cb callback, void *user_data)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_dp_set_terminated_cb(dp, callback, user_data);
+}
+
+API int vine_dp_unset_terminated_cb(vine_dp_h dp)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_dp_unset_terminated_cb(dp);
+}
+
+API int vine_dp_open(vine_dp_h dp, vine_dp_opened_cb callback, void *user_data)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_dp_open(dp, callback, user_data);
+}
+
+API int vine_dp_close(vine_dp_h dp)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_dp_close(dp);
+}
+
+API int vine_dp_send(vine_dp_h dp, unsigned char *buf, size_t len)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_dp_send(dp, buf, len);
+}
+
+API int vine_dp_recv(vine_dp_h dp,
+ unsigned char *buf, size_t buf_len, size_t *read_len)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_dp_recv(dp, buf, buf_len, read_len);
+}
+
+API int vine_dp_set_received_cb(vine_dp_h dp, vine_dp_received_cb callback, void *user_data)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_dp_set_received_cb(dp, callback, user_data);
+}
+
+API int vine_dp_unset_received_cb(vine_dp_h dp)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_dp_unset_received_cb(dp);
+}
+
+API int vine_security_create(vine_security_h *security)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_security_create(security);
+}
+
+API int vine_security_destroy(vine_security_h security)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_security_destroy(security);
+}
+
+API int vine_security_set_type(vine_security_h security, vine_security_type_e type)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_security_set_type(security, type);
+}
+
+API int vine_security_get_type(vine_security_h security, vine_security_type_e *type)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_security_get_type(security, type);
+}
+
+API int vine_security_set_tls_version(vine_security_h security,
+ vine_security_tls_version_e version)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_security_set_tls_version(security, version);
+}
+
+API int vine_security_get_tls_version(vine_security_h security,
+ vine_security_tls_version_e *version)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_security_get_tls_version(security, version);
+}
+
+API int vine_security_set_verification_flags(vine_security_h security, int flags)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_security_set_verification_flags(security, flags);
+}
+
+API int vine_security_get_verification_flags(vine_security_h security, int*flags)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_security_get_verification_flags(security, flags);
+}
+
+API int vine_security_set_ca_path(vine_security_h security, const char *ca_path)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_security_set_ca_path(security, ca_path);
+}
+
+API int vine_security_get_ca_path(vine_security_h security, char **ca_path)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_security_get_ca_path(security, ca_path);
+}
+
+API int vine_security_set_cert_path(vine_security_h security, const char *cert_path)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_security_set_cert_path(security, cert_path);
+}
+
+API int vine_security_get_cert_path(vine_security_h security, char **cert_path)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_security_get_cert_path(security, cert_path);
+}
+
+API int vine_security_set_private_key(vine_security_h security, const char *key_path)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_security_set_private_key(security, key_path);
+}
+
+API int vine_security_get_private_key(vine_security_h security, char **key_path)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_security_get_private_key(security, key_path);
+}
+
+API int vine_security_set_psk(vine_security_h security, const char *psk)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_security_set_psk(security, psk);
+}
+
+API int vine_security_get_psk(vine_security_h security, char **psk)
+{
+ __VINE_FUNC_ENTER__;
+ CHECK_FEATURE_SUPPORTED;
+
+ return _vine_security_get_psk(security, psk);
+}
--- /dev/null
+# Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+SET(VINE_UNITTEST "vine-unittest")
+LINK_DIRECTORIES(${CMAKE_BINARY_DIR})
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -Wall -fPIE")
+SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -pie")
+
+INCLUDE_DIRECTORIES(
+ ${CMAKE_SOURCE_DIR}/include
+ ${CMAKE_SOURCE_DIR}/src/include
+ ${PREBUILT_THIRD_PARTY_INCLUDE_PATH}
+ ${VINE_LOGGER_PATH}
+ ${fw_name_deps_INCLUDE_DIRS}
+)
+
+IF(ANDROID)
+ SET(GOOGLETEST_ROOT ${ANDROID_NDK}/sources/third_party/googletest/)
+ ADD_LIBRARY(gtest STATIC ${GOOGLETEST_ROOT}/src/gtest_main.cc ${GOOGLETEST_ROOT}/src/gtest-all.cc)
+ TARGET_INCLUDE_DIRECTORIES(gtest PRIVATE ${GOOGLETEST_ROOT})
+ TARGET_INCLUDE_DIRECTORIES(gtest PUBLIC ${GOOGLETEST_ROOT}/include)
+ SET(GTEST gtest)
+ELSE(ANDROID)
+ SET(GTEST gmock)
+ENDIF(ANDROID)
+
+FILE(GLOB UNITTEST_SRCS *.cpp mocks/*.cpp ${CMAKE_SOURCE_DIR}/src/*.cpp)
+ADD_EXECUTABLE(${VINE_UNITTEST} ${UNITTEST_SRCS})
+TARGET_LINK_LIBRARIES(${VINE_UNITTEST}
+ ${VINE_LOGGER}
+ ${VINE_DEPS_LIB}
+ ${fw_name_deps_LIBRARIES}
+ ${GTEST}
+ dl
+)
+
+SET_TARGET_PROPERTIES(${VINE_UNITTEST} PROPERTIES
+ COMPILE_FLAGS "-fPIE"
+ LINK_FLAGS "-Wl,\
+--wrap=calloc,\
+--wrap=malloc,\
+--wrap=strdup,\
+--wrap=dlopen,\
+--wrap=dlclose,\
+--wrap=dlsym")
+
+INSTALL(TARGETS ${VINE_UNITTEST} DESTINATION "${BIN_DIR}")
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "vine-mock-data-path-plugin.h"
+
+static vine_dp_plugin_callbacks __mock_callbacks;
+
+typedef struct {
+ void *wsi;
+ void *vh;
+ void *user;
+} __mock_data_path_plugin_s;
+
+static bool g_is_init = false;
+
+static int __mock_data_path_plugin_init(void)
+{
+ if (g_is_init)
+ return 0;
+ g_is_init = true;
+
+ return 0;
+}
+
+static void __mock_data_path_plugin_deinit(void)
+{
+ if (!g_is_init)
+ return;
+ g_is_init = false;
+}
+
+static int __mock_data_path_plugin_open(vine_dp_plugin_h handle, int addr_family, int port,
+ const char *iface_name, int max_conn, vine_dp_ssl ssl)
+{
+ if (!handle)
+ return VINE_DATA_PATH_ERROR_INVALID_PARAMETER;
+
+ return VINE_DATA_PATH_ERROR_NONE;
+}
+
+static int __mock_data_path_plugin_connect(vine_dp_plugin_h handle,
+ int addr_family, const char *ip, int port, const char *iface_name, vine_dp_ssl ssl)
+{
+ if (!handle)
+ return VINE_DATA_PATH_ERROR_INVALID_PARAMETER;
+
+ return VINE_DATA_PATH_ERROR_NONE;
+}
+
+static int __mock_data_path_plugin_read(vine_dp_plugin_h handle, unsigned char *buf, size_t len)
+{
+ if (!handle || !buf || len <= 0)
+ return VINE_DATA_PATH_ERROR_INVALID_PARAMETER;
+
+ return VINE_DATA_PATH_ERROR_NONE;
+}
+
+static int __mock_data_path_plugin_write(vine_dp_plugin_h handle, unsigned char *buf, size_t len)
+{
+ if (!handle || !buf || len <= 0)
+ return VINE_DATA_PATH_ERROR_INVALID_PARAMETER;
+
+ return VINE_DATA_PATH_ERROR_NONE;
+}
+
+static int __mock_data_path_plugin_close(vine_dp_plugin_h handle)
+{
+ if (!handle)
+ return VINE_DATA_PATH_ERROR_INVALID_PARAMETER;
+
+ return VINE_DATA_PATH_ERROR_NONE;
+}
+
+static void __mock_data_path_plugin_process_event(int fd, int events)
+{
+ return;
+}
+
+void __mock_data_path_plugin_register_callbacks(vine_dp_plugin_callbacks handlers)
+{
+ __mock_callbacks = handlers;
+}
+
+static int __mock_data_path_plugin_create(vine_dp_plugin_h *handle, void *plugin_data, void *user)
+{
+ if (!handle)
+ return VINE_DATA_PATH_ERROR_INVALID_PARAMETER;
+
+ __mock_data_path_plugin_s *dp
+ = (__mock_data_path_plugin_s *)calloc(1, sizeof(__mock_data_path_plugin_s));
+ if (!dp)
+ return VINE_DATA_PATH_ERROR_OUT_OF_MEMORY;
+
+ dp->wsi = NULL;
+ dp->vh = NULL;
+ dp->user = user;
+
+ *handle = dp;
+
+ return VINE_DATA_PATH_ERROR_NONE;
+}
+
+static int __mock_data_path_plugin_destroy(vine_dp_plugin_h handle)
+{
+ if (!handle)
+ return VINE_DATA_PATH_ERROR_INVALID_PARAMETER;
+
+ __mock_data_path_plugin_s *dp = (__mock_data_path_plugin_s *)handle;
+
+ dp->wsi = NULL;
+ dp->vh = NULL;
+ dp->user = NULL;
+
+ free(dp);
+
+ return VINE_DATA_PATH_ERROR_NONE;
+}
+
+void __mock_vine_data_path_plugin_init(vine_dp_plugin_fn *fn)
+{
+ fn->init = __mock_data_path_plugin_init;
+ fn->deinit = __mock_data_path_plugin_deinit;
+ fn->register_callbacks = __mock_data_path_plugin_register_callbacks;
+ fn->process_event = __mock_data_path_plugin_process_event;
+
+ fn->create = __mock_data_path_plugin_create;
+ fn->destroy = __mock_data_path_plugin_destroy;
+ fn->open = __mock_data_path_plugin_open;
+ fn->connect = __mock_data_path_plugin_connect;
+ fn->read = __mock_data_path_plugin_read;
+ fn->write = __mock_data_path_plugin_write;
+ fn->close = __mock_data_path_plugin_close;
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "vine-data-path-plugin.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __mock_vine_data_path_plugin_init(vine_dp_plugin_fn *fn);
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "vine-mock-dl.h"
+#include "vine-mock-dns-sd.h"
+#include "vine-mock-data-path-plugin.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static bool __vine_mock_dlopen = true;
+
+extern void *__real_dlopen(const char *filename, int flags);
+extern void *__real_dlsym(void *handle, const char *symbol);
+
+/*
+typedef struct {
+ int (*init)(void **plugin_handle, void *disc_handle);
+ int (*deinit)(void *plugin_handle);
+ int (*publish)(void *plugin_handle,
+ const char *service_type, const char *service_name,
+ int port, void *attributes);
+ // TODO
+ void (*register_callbacks)(vine_disc_event_handler handlers);
+} __vine_discovery_plugin;
+*/
+
+void vine_mock_set_dlopen(bool result)
+{
+ __vine_mock_dlopen = result;
+}
+
+void *__wrap_dlopen(const char *filename, int flags)
+{
+ return __vine_mock_dlopen ? __real_dlopen(filename, flags) : NULL;
+}
+
+int __wrap_dlclose(void *handle)
+{
+ return 0;
+}
+
+void *__wrap_dlsym(void *handle, const char *symbol)
+{
+ if (strcmp(symbol, DISC_PLUGIN_INIT) == 0) {
+ return (void *)__mock_vine_disc_plugin_init;
+ } else if (strcmp(symbol, DATA_PATH_PLUGIN_INIT) == 0) {
+ return (void *)__mock_vine_data_path_plugin_init;
+ }
+
+ return __real_dlsym(handle, symbol);
+}
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void vine_mock_set_dlopen(bool result);
+
+#define DISC_PLUGIN_INIT "vine_disc_plugin_init"
+#define DATA_PATH_PLUGIN_INIT "vine_data_path_plugin_init"
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <map>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "vine-mock-dns-sd.h"
+
+static vine_disc_plugin_callbacks __mock_event_handlers;
+
+typedef struct {
+ void *service_ref;
+ int fd;
+ void *user_data;
+} __vine_dns_sd_s;
+
+vine_disc_error __mock_disc_plugin_init(void **plugin_handle, void *disc_handle)
+{
+ __vine_dns_sd_s *handle = (__vine_dns_sd_s *)calloc(1, sizeof(__vine_dns_sd_s));
+
+ if (handle == NULL)
+ return VINE_DISC_ERROR_OUT_OF_MEMORY;
+
+ handle->service_ref = NULL;
+ handle->fd = -1;
+ handle->user_data = disc_handle;
+ *plugin_handle = handle;
+ return VINE_DISC_ERROR_NONE;
+}
+
+void __mock_disc_plugin_deinit(void *plugin_handle)
+{
+ free(plugin_handle);
+}
+
+vine_disc_error __mock_disc_plugin_publish(void *plugin_handle,
+ const char *service_type, const char *service_name,
+ int port, const std::map<std::string, std::string> &attributes,
+ const char *iface_name)
+{
+ return VINE_DISC_ERROR_NONE;
+}
+
+vine_disc_error __mock_disc_stop_publish(void *plugin_handle)
+{
+ return VINE_DISC_ERROR_NONE;
+}
+
+vine_disc_error __mock_disc_plugin_subscribe(void *plugin_handle,
+ const char *service_type, const char *iface_name)
+{
+ return VINE_DISC_ERROR_NONE;
+}
+
+vine_disc_error __mock_disc_plugin_stop_subscribe(void *plugin_handle)
+{
+ return VINE_DISC_ERROR_NONE;
+}
+
+vine_disc_error __mock_disc_plugin_resolve_ip(void *plugin_handle,
+ const char *service_type, const char *service_name,
+ const char *host_name, const char *iface_name)
+{
+ return VINE_DISC_ERROR_NONE;
+}
+
+vine_disc_error __mock_disc_plugin_cancel_resolve_ip(void *plugin_handle)
+{
+ return VINE_DISC_ERROR_NONE;
+}
+
+vine_disc_error __mock_disc_plugin_process_event(void *plugin_handle, int fd)
+{
+ return VINE_DISC_ERROR_NONE;
+}
+
+void __mock_disc_plugin_register_callbacks(vine_disc_plugin_callbacks handlers)
+{
+ __mock_event_handlers.published_cb = handlers.published_cb;
+}
+
+void __mock_vine_disc_plugin_init(vine_disc_plugin_fn *fn)
+{
+ fn->init = __mock_disc_plugin_init;
+ fn->deinit = __mock_disc_plugin_deinit;
+ fn->publish = __mock_disc_plugin_publish;
+ fn->stop_publish = __mock_disc_stop_publish;
+ fn->subscribe = __mock_disc_plugin_subscribe;
+ fn->stop_subscribe = __mock_disc_plugin_stop_subscribe;
+ fn->resolve_ip = __mock_disc_plugin_resolve_ip;
+ fn->cancel_resolve_ip = __mock_disc_plugin_cancel_resolve_ip;
+ fn->register_callbacks = __mock_disc_plugin_register_callbacks;
+ fn->process_event = __mock_disc_plugin_process_event;
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "vine-disc-plugin.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __mock_vine_disc_plugin_init(vine_disc_plugin_fn *fn);
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <memory.h>
+
+#include "vine-mock-memory.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static bool __vine_mock_memory_result;
+
+extern void *__real_calloc(size_t nmemb, size_t size);
+extern void *__real_malloc(size_t size);
+extern char *__real_strdup(const char *str);
+
+void vine_mock_set_memory_result(bool result)
+{
+ __vine_mock_memory_result = result;
+}
+
+void *__wrap_calloc(size_t nmemb, size_t size)
+{
+ return __vine_mock_memory_result ? __real_calloc(nmemb, size) : NULL;
+}
+
+char *__wrap_strdup(const char *str)
+{
+ return __vine_mock_memory_result ? __real_strdup(str) : NULL;
+}
+
+void *__wrap_malloc(size_t size)
+{
+ return __vine_mock_memory_result ? __real_malloc(size) : NULL;
+}
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void vine_mock_set_memory_result(bool result);
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+int system_info_get_platform_bool(const char *key, bool *value)
+{
+ *value = true;
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "vine.h"
+
+#include "mocks/vine-mock-memory.h"
+
+class VineCapaTest : public ::testing::Test {
+protected:
+ void SetUp() override
+ {
+ vine_initialize();
+ }
+
+ void TearDown() override
+ {
+ vine_deinitialize();
+ }
+};
+
+TEST_F(VineCapaTest, GetCapabilitiesN)
+{
+ int capa;
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_get_capabilities((vine_capability_type_e)-1, &capa));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_get_capabilities(VINE_CAPA_DISCOVERY_METHODS, NULL));
+}
+
+TEST_F(VineCapaTest, GetCapabilitiesP)
+{
+ int capa;
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_get_capabilities(VINE_CAPA_MAX_CONNECTIONS, &capa));
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "vine.h"
+
+#include "mocks/vine-mock-memory.h"
+
+class VineDpTest : public ::testing::Test {
+protected:
+ vine_dp_h server_dp;
+ vine_dp_h client_dp;
+ vine_dp_h pubsub_dp;
+ vine_session_h session;
+
+ void SetUp() override
+ {
+ vine_mock_set_memory_result(true);
+ vine_initialize();
+ vine_session_create(&session);
+ vine_dp_create(session, VINE_DP_TYPE_SERVER, &server_dp);
+ vine_dp_create(session, VINE_DP_TYPE_CLIENT, &client_dp);
+ vine_dp_create(session, VINE_DP_TYPE_PUBSUB, &pubsub_dp);
+ }
+
+ void TearDown() override
+ {
+ vine_mock_set_memory_result(true);
+ vine_session_destroy(session);
+ vine_dp_destroy(server_dp);
+ vine_dp_destroy(client_dp);
+ vine_dp_destroy(pubsub_dp);
+ vine_deinitialize();
+ }
+};
+
+static void __accepted_cb(vine_dp_h dp, vine_dp_h accepted_dp, void *user_data)
+{
+}
+
+static void __opened_cb(vine_dp_h dp, vine_error_e result, void *user_data)
+{
+}
+
+static void __received_cb(vine_dp_h dp, size_t received_len, void *user_data)
+{
+}
+
+static void __terminated_cb(vine_dp_h dp, void *user_data)
+{
+}
+
+TEST_F(VineDpTest, CreateN)
+{
+ vine_dp_h dp;
+
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_create(NULL, VINE_DP_TYPE_SERVER, &dp));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_create(session, VINE_DP_TYPE_UNKNOWN, &dp));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_create(session, VINE_DP_TYPE_SERVER, NULL));
+}
+
+TEST_F(VineDpTest, CreateP)
+{
+ vine_dp_h dp;
+
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_dp_create(session, VINE_DP_TYPE_SERVER, &dp));
+ vine_dp_destroy(dp);
+}
+
+TEST_F(VineDpTest, DestroyN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_destroy(NULL));
+}
+
+TEST_F(VineDpTest, DestroyP)
+{
+ vine_dp_h dp;
+
+ vine_dp_create(session, VINE_DP_TYPE_SERVER, &dp);
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_dp_destroy(dp));
+}
+
+TEST_F(VineDpTest, SetRemoteIpN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_set_remote_ip(client_dp, VINE_ADDRESS_FAMILY_IPV4, NULL));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_set_remote_ip(NULL, VINE_ADDRESS_FAMILY_IPV4, "192.168.1.123"));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_set_remote_ip(client_dp, VINE_ADDRESS_FAMILY_IPV6, "192.168.1.123"));
+ EXPECT_EQ(VINE_ERROR_INVALID_OPERATION,
+ vine_dp_set_remote_ip(server_dp, VINE_ADDRESS_FAMILY_IPV4, "192.168.1.123"));
+}
+
+TEST_F(VineDpTest, SetRemoteIpP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_dp_set_remote_ip(client_dp, VINE_ADDRESS_FAMILY_IPV4, "192.168.1.123"));
+}
+
+TEST_F(VineDpTest, SetPortN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_set_port(client_dp, -1));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_set_port(NULL, 12345));
+}
+
+TEST_F(VineDpTest, SetPortP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_dp_set_port(client_dp, 12345));
+}
+
+TEST_F(VineDpTest, GetPortN)
+{
+ int port;
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_get_port(client_dp, NULL));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_get_port(NULL, &port));
+}
+
+TEST_F(VineDpTest, GetPortP)
+{
+ int port;
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_dp_get_port(server_dp, &port));
+}
+
+TEST_F(VineDpTest, SetTopicN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_set_topic(pubsub_dp, NULL));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_set_topic(NULL, "test"));
+ EXPECT_EQ(VINE_ERROR_INVALID_OPERATION,
+ vine_dp_set_topic(client_dp, "test"));
+}
+
+TEST_F(VineDpTest, SetTopicP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_dp_set_topic(pubsub_dp, "test"));
+}
+
+TEST_F(VineDpTest, SetAcceptedCbN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_set_accepted_cb(server_dp, NULL, NULL));
+ EXPECT_EQ(VINE_ERROR_INVALID_OPERATION,
+ vine_dp_set_accepted_cb(client_dp, __accepted_cb, NULL));
+}
+
+TEST_F(VineDpTest, SetAcceptedCbP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_dp_set_accepted_cb(server_dp, __accepted_cb, NULL));
+}
+
+TEST_F(VineDpTest, UnsetAcceptedCbN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_unset_accepted_cb(NULL));
+ EXPECT_EQ(VINE_ERROR_INVALID_OPERATION,
+ vine_dp_unset_accepted_cb(client_dp));
+}
+
+TEST_F(VineDpTest, UnsetAcceptedCbP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_dp_unset_accepted_cb(server_dp));
+}
+
+TEST_F(VineDpTest, OpenN1)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_dp_open(pubsub_dp, __opened_cb, NULL));
+ EXPECT_EQ(VINE_ERROR_NOW_IN_PROGRESS,
+ vine_dp_open(pubsub_dp, __opened_cb, NULL));
+}
+
+TEST_F(VineDpTest, OpenN2)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_open(pubsub_dp, NULL, NULL));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_open(NULL, __opened_cb, NULL));
+}
+
+TEST_F(VineDpTest, OpenP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_dp_open(server_dp, __opened_cb, NULL));
+}
+
+TEST_F(VineDpTest, SendN)
+{
+ char buf[12] = "TestMessage";
+
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_send(client_dp, NULL, 12));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_send(NULL, (unsigned char *)buf, 12));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_send(NULL, NULL, 12));
+}
+
+TEST_F(VineDpTest, RecvN)
+{
+ unsigned char buf[12];
+ size_t recv_bytes;
+
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_recv(NULL, buf, 12, &recv_bytes));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_recv(client_dp, NULL, 12, &recv_bytes));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_recv(NULL, buf, 12, NULL));
+}
+
+TEST_F(VineDpTest, SetReceivedCbN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_set_received_cb(NULL, __received_cb, NULL));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_set_received_cb(client_dp, NULL, NULL));
+}
+
+TEST_F(VineDpTest, SetReceivedCbP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_dp_set_received_cb(client_dp, __received_cb, NULL));
+}
+
+TEST_F(VineDpTest, UnsetReceivedCbN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_unset_received_cb(NULL));
+}
+
+TEST_F(VineDpTest, UnsetReceivedCbP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_dp_unset_received_cb(client_dp));
+}
+
+TEST_F(VineDpTest, CloseN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_close(NULL));
+}
+
+TEST_F(VineDpTest, SetTerminatedCbN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_set_terminated_cb(NULL, __terminated_cb, NULL));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_set_terminated_cb(client_dp, NULL, NULL));
+}
+
+TEST_F(VineDpTest, SetTerminatedCbP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_dp_set_terminated_cb(client_dp, __terminated_cb, NULL));
+}
+
+TEST_F(VineDpTest, UnsetTerminatedCbN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_unset_terminated_cb(NULL));
+}
+
+TEST_F(VineDpTest, UnsetTerminatedCbP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_dp_unset_terminated_cb(client_dp));
+}
+
+TEST_F(VineDpTest, SetSecurityN)
+{
+ vine_security_h security;
+
+ vine_security_create(&security);
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_set_security(NULL, security));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_set_security(client_dp, NULL));
+ vine_security_destroy(security);
+}
+
+TEST_F(VineDpTest, SetSecurityP)
+{
+ vine_security_h security;
+
+ vine_security_create(&security);
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_dp_set_security(client_dp, security));
+ vine_security_destroy(security);
+}
+
+TEST_F(VineDpTest, SetIfaceNameN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_set_iface_name(server_dp, NULL));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_set_iface_name(NULL, "wlan0"));
+}
+
+TEST_F(VineDpTest, SetIfaceNameP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_dp_set_iface_name(server_dp, "wlan0"));
+}
+
+TEST_F(VineDpTest, SetAddressFamilyN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_set_address_family(server_dp, (vine_address_family_e)-1));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_set_address_family(NULL, VINE_ADDRESS_FAMILY_IPV4));
+}
+
+TEST_F(VineDpTest, SetAddressFamilyP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_dp_set_address_family(server_dp, VINE_ADDRESS_FAMILY_IPV4));
+}
+
+TEST_F(VineDpTest, SetMaxConnectionsN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_set_max_connections(server_dp, -1));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_dp_set_max_connections(NULL, 1024));
+}
+
+TEST_F(VineDpTest, SetMaxConnectionsP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_dp_set_max_connections(server_dp, 30));
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "vine.h"
+
+#include "mocks/vine-mock-dl.h"
+#include "mocks/vine-mock-memory.h"
+
+class VineInitTest : public ::testing::Test {
+protected:
+ void SetUp() override
+ {
+ vine_mock_set_memory_result(true);
+ vine_mock_set_dlopen(true);
+ }
+
+ void TearDown() override
+ {
+ vine_mock_set_memory_result(true);
+ vine_mock_set_dlopen(true);
+ }
+};
+
+TEST_F(VineInitTest, DeinitN)
+{
+ EXPECT_EQ(VINE_ERROR_NOT_INITIALIZED, vine_deinitialize());
+}
+
+TEST_F(VineInitTest, InitDeinitP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE, vine_initialize());
+ EXPECT_EQ(VINE_ERROR_NONE, vine_deinitialize());
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "vine.h"
+#include "vine-log.h"
+
+#include "mocks/vine-mock-dl.h"
+#include "mocks/vine-mock-memory.h"
+
+extern int UNITTEST_LOG_LEVEL;
+extern void vine_unittest_logger(int log_level, const char *log);
+
+class VineLogTest : public ::testing::Test {
+protected:
+ void SetUp() override
+ {
+ vine_mock_set_memory_result(true);
+ vine_mock_set_dlopen(true);
+ }
+
+ void TearDown() override
+ {
+ vine_mock_set_memory_result(true);
+ vine_mock_set_dlopen(true);
+ vine_set_log_level(UNITTEST_LOG_LEVEL);
+ vine_set_logger(vine_unittest_logger);
+ }
+};
+
+static void __logger(int log_level, const char *log)
+{
+ printf("LOG_TEST %d %s\n", log_level, log);
+}
+
+TEST_F(VineLogTest, LogP)
+{
+ vine_set_log_level(VINE_LOG_DEBUG|VINE_LOG_INFO|VINE_LOG_ERROR);
+ vine_set_logger(__logger);
+ vine_initialize();
+ vine_deinitialize();
+ vine_set_log_level(0);
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include "vine-log.h"
+
+int UNITTEST_LOG_LEVEL = 0;
+
+void vine_unittest_logger(int log_level, const char *log)
+{
+ printf("%d %s\n", log_level, log);
+}
+
+int main(int argc, char **argv)
+{
+ if (argc == 2 && strcmp(argv[1], "-d") == 0) {
+ UNITTEST_LOG_LEVEL = VINE_LOG_DEBUG|VINE_LOG_INFO|VINE_LOG_ERROR;
+ vine_set_log_level(UNITTEST_LOG_LEVEL);
+ vine_set_logger(vine_unittest_logger);
+ }
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "vine.h"
+
+#include "vine-unittest-utils.h"
+#include "mocks/vine-mock-dl.h"
+#include "mocks/vine-mock-memory.h"
+
+class VineSecurityTest : public ::testing::Test {
+protected:
+ void SetUp() override
+ {
+ vine_mock_set_memory_result(true);
+
+ vine_initialize();
+ vine_security_create(&security);
+ }
+
+ void TearDown() override
+ {
+ vine_mock_set_memory_result(true);
+ vine_mock_set_dlopen(true);
+
+ vine_security_destroy(security);
+ vine_deinitialize();
+ }
+
+ vine_security_h security;
+};
+
+TEST_F(VineSecurityTest, CreateN)
+{
+ vine_security_h local_security;
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_create(NULL));
+
+ vine_mock_set_memory_result(false);
+ EXPECT_EQ(VINE_ERROR_OUT_OF_MEMORY,
+ vine_security_create(&local_security));
+}
+
+TEST_F(VineSecurityTest, DestroyN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_destroy(NULL));
+}
+
+TEST_F(VineSecurityTest, SetTypeN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_set_type(security, (vine_security_type_e) -1));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_set_type(NULL, VINE_SECURITY_TYPE_TLS));
+}
+
+TEST_F(VineSecurityTest, SetTypeP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_security_set_type(security, VINE_SECURITY_TYPE_TLS));
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_security_set_type(security, VINE_SECURITY_TYPE_PSK_OVER_TLS));
+}
+
+TEST_F(VineSecurityTest, GetTypeN)
+{
+ vine_security_type_e type;
+
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_get_type(security, NULL));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_get_type(NULL, &type));
+}
+
+TEST_F(VineSecurityTest, GetTypeP)
+{
+ vine_security_type_e type;
+
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_security_get_type(security, &type));
+}
+
+TEST_F(VineSecurityTest, SetTlsVersionN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_set_tls_version(security, (vine_security_tls_version_e) - 1));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_set_tls_version(NULL, VINE_SECURITY_TLS_VERSION_1_1));
+}
+
+TEST_F(VineSecurityTest, SetTlsVersionP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_security_set_tls_version(security, VINE_SECURITY_TLS_VERSION_1_1));
+}
+
+TEST_F(VineSecurityTest, GetTlsVersionN)
+{
+ vine_security_tls_version_e version;
+
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_get_tls_version(security, NULL));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_get_tls_version(NULL, &version));
+}
+
+TEST_F(VineSecurityTest, GetTlsVersionP)
+{
+ vine_security_tls_version_e version;
+
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_security_get_tls_version(security, &version));
+}
+
+TEST_F(VineSecurityTest, SetVerificationFlagsN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_set_verification_flags(security, 12));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_set_verification_flags(NULL,
+ VINE_SECURITY_VERIFICATION_FLAG_ALLOW_SELF_SIGNED));
+}
+
+TEST_F(VineSecurityTest, SetVerificationFlagsP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_security_set_verification_flags(security,
+ VINE_SECURITY_VERIFICATION_FLAG_ALLOW_SELF_SIGNED));
+}
+
+TEST_F(VineSecurityTest, GetVerificationFlagsN)
+{
+ int flags;
+
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_get_verification_flags(security, NULL));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_get_verification_flags(NULL, &flags));
+}
+
+TEST_F(VineSecurityTest, GetVerificationFlagsP)
+{
+ int flags;
+
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_security_get_verification_flags(security, &flags));
+}
+
+TEST_F(VineSecurityTest, SetCaPathN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_set_ca_path(security, NULL));
+
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_set_ca_path(NULL, "/etc/ssl/ca.pem"));
+}
+
+TEST_F(VineSecurityTest, SetCaPathP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_security_set_ca_path(security, "/etc/ssl/ca.pem"));
+}
+
+TEST_F(VineSecurityTest, GetCaPathN)
+{
+ char *path = NULL;
+
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_get_ca_path(security, NULL));
+
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_get_ca_path(NULL, &path));
+}
+
+TEST_F(VineSecurityTest, GetCaPathP)
+{
+ char *path = NULL;
+
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_security_get_ca_path(security, &path));
+ free(path);
+}
+
+TEST_F(VineSecurityTest, SetCertPathN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_set_cert_path(security, NULL));
+
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_set_cert_path(NULL, "/opt/apps/vine/certs/server.cert"));
+}
+
+TEST_F(VineSecurityTest, SetCertPathP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_security_set_cert_path(security, "/opt/apps/vine/certs/server.cert"));
+}
+
+TEST_F(VineSecurityTest, GetCertPathN)
+{
+ char *path = NULL;
+
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_get_cert_path(security, NULL));
+
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_get_cert_path(NULL, &path));
+}
+
+TEST_F(VineSecurityTest, GetCertPathP)
+{
+ char *path = NULL;
+
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_security_get_cert_path(security, &path));
+ free(path);
+}
+
+TEST_F(VineSecurityTest, SetKeyPathN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_set_private_key(security, NULL));
+
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_set_private_key(NULL, "/opt/apps/vine/certs/server-private.key"));
+}
+
+TEST_F(VineSecurityTest, SetKeyPathP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_security_set_private_key(security, "/opt/apps/vine/certs/server-private.key"));
+}
+
+TEST_F(VineSecurityTest, GetKeyPathN)
+{
+ char *path = NULL;
+
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_get_private_key(security, NULL));
+
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_get_private_key(NULL, &path));
+}
+
+TEST_F(VineSecurityTest, GetKeyPathP)
+{
+ char *path = NULL;
+
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_security_get_private_key(security, &path));
+ free(path);
+}
+
+TEST_F(VineSecurityTest, SetPskN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_set_psk(security, NULL));
+
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_set_psk(NULL, "sooiCmUb4N9fSBSh8g1qgmEe2F8tHfhqoQO4dLJjKZE="));
+}
+
+TEST_F(VineSecurityTest, SetPskP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_security_set_psk(security, "sooiCmUb4N9fSBSh8g1qgmEe2F8tHfhqoQO4dLJjKZE="));
+}
+
+TEST_F(VineSecurityTest, GetPskN)
+{
+ char *psk = NULL;
+
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_get_psk(security, NULL));
+
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_security_get_psk(NULL, &psk));
+}
+
+TEST_F(VineSecurityTest, GetPskP)
+{
+ char *psk = NULL;
+
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_security_get_psk(security, &psk));
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "vine.h"
+
+#include "vine-unittest-utils.h"
+#include "mocks/vine-mock-dl.h"
+#include "mocks/vine-mock-memory.h"
+
+class VineServiceTest : public ::testing::Test {
+protected:
+ void SetUp() override
+ {
+ vine_mock_set_memory_result(true);
+ vine_mock_set_dlopen(true);
+
+ vine_initialize();
+ vine_service_create(&service);
+ }
+
+ void TearDown() override
+ {
+ vine_mock_set_memory_result(true);
+ vine_mock_set_dlopen(true);
+
+ vine_service_destroy(service);
+ vine_deinitialize();
+ }
+
+ vine_service_h service;
+};
+
+TEST_F(VineServiceTest, CreateN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_create(NULL));
+}
+
+TEST_F(VineServiceTest, DestroyN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_destroy(NULL));
+}
+
+TEST_F(VineServiceTest, CreateDestroyP)
+{
+ vine_service_h local_service;
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_service_create(&local_service));
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_service_destroy(local_service));
+}
+
+TEST_F(VineServiceTest, CloneN)
+{
+ vine_service_h cloned;
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_clone(NULL, &cloned));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_clone(service, NULL));
+}
+
+TEST_F(VineServiceTest, CloneP)
+{
+ vine_service_h cloned;
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_service_clone(service, &cloned));
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_service_destroy(cloned));
+}
+
+TEST_F(VineServiceTest, SetServiceTypeN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_set_type(NULL, SERVICE_TYPE));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_set_type(service, NULL));
+
+ char long_type[100] = {0, };
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_set_type(service, long_type));
+}
+
+TEST_F(VineServiceTest, SetServiceTypeP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_service_set_type(service, SERVICE_TYPE));
+}
+
+TEST_F(VineServiceTest, GetServiceTypeN)
+{
+ char *service_type;
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_get_type(NULL, &service_type));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_get_type(service, NULL));
+
+ vine_mock_set_memory_result(false);
+ EXPECT_EQ(VINE_ERROR_OUT_OF_MEMORY,
+ vine_service_get_type(service, &service_type));
+}
+
+TEST_F(VineServiceTest, GetServiceTypeP)
+{
+ char *service_type;
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_service_get_type(service, &service_type));
+ free(service_type);
+}
+
+TEST_F(VineServiceTest, SetServiceNameN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_set_name(NULL, SERVICE_NAME));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_set_name(service, NULL));
+
+ char long_name[100] = {0, };
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_set_name(service, long_name));
+}
+
+TEST_F(VineServiceTest, SetServiceNameP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_service_set_name(service, SERVICE_NAME));
+}
+
+TEST_F(VineServiceTest, GetServiceNameN)
+{
+ char *service_name;
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_get_name(NULL, &service_name));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_get_name(service, NULL));
+
+ vine_mock_set_memory_result(false);
+ EXPECT_EQ(VINE_ERROR_OUT_OF_MEMORY,
+ vine_service_get_name(service, &service_name));
+}
+
+TEST_F(VineServiceTest, GetServiceNameP)
+{
+ char *service_name;
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_service_get_name(service, &service_name));
+ free(service_name);
+}
+
+TEST_F(VineServiceTest, AddAttributeN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_add_attribute(NULL, KEY, VALUE));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_add_attribute(service, NULL, VALUE));
+
+ char long_key[10] = {0, };
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_add_attribute(service, long_key, VALUE));
+
+ // Add a duplicate <key, value> pair
+ vine_service_add_attribute(service, KEY, VALUE);
+ EXPECT_EQ(VINE_ERROR_INVALID_OPERATION,
+ vine_service_add_attribute(service, KEY, VALUE));
+}
+
+TEST_F(VineServiceTest, RemoveAttributeN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_remove_attribute(NULL, KEY));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_remove_attribute(service, NULL));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_remove_attribute(service, KEY));
+}
+
+TEST_F(VineServiceTest, AddRemoveAttributeP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_service_add_attribute(service, KEY, VALUE));
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_service_add_attribute(service, KEY2, NULL));
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_service_remove_attribute(service, KEY));
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_service_remove_attribute(service, KEY2));
+}
+
+static bool __foreach_attr(const char *key, const char *value, void *user_data)
+{
+ return true;
+}
+
+TEST_F(VineServiceTest, GetAttributeN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_foreach_attribute(NULL, __foreach_attr, NULL));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_foreach_attribute(service, NULL, NULL));
+}
+
+TEST_F(VineServiceTest, GetAttributeP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_service_foreach_attribute(service, __foreach_attr, NULL));
+}
+
+TEST_F(VineServiceTest, SetPortN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_set_port(NULL, PORT));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_set_port(service, -1));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_set_port(service, 65536));
+}
+
+TEST_F(VineServiceTest, SetPortP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_service_set_port(service, PORT));
+}
+
+TEST_F(VineServiceTest, GetPortN)
+{
+ int port;
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_get_port(NULL, &port));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_service_get_port(service, NULL));
+}
+
+TEST_F(VineServiceTest, GetPortP)
+{
+ int port;
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_service_get_port(service, &port));
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "vine.h"
+
+#include "vine-unittest-utils.h"
+#include "mocks/vine-mock-dl.h"
+#include "mocks/vine-mock-memory.h"
+
+class VineSessionTest : public ::testing::Test {
+protected:
+ void SetUp() override
+ {
+ vine_mock_set_memory_result(true);
+ vine_mock_set_dlopen(true);
+
+ vine_initialize();
+ vine_session_create(&session);
+ vine_security_create(&security);
+ service = _create_dummy_service();
+ }
+
+ void TearDown() override
+ {
+ vine_mock_set_memory_result(true);
+ vine_mock_set_dlopen(true);
+
+ _destroy_dummy_service(service);
+ vine_security_destroy(security);
+ vine_session_destroy(session);
+ vine_deinitialize();
+ }
+
+ vine_session_h session;
+ vine_service_h service;
+ vine_security_h security;
+};
+
+TEST_F(VineSessionTest, CreateN)
+{
+ vine_session_h local_session;
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_session_create(NULL));
+
+ vine_mock_set_memory_result(false);
+ EXPECT_EQ(VINE_ERROR_OUT_OF_MEMORY,
+ vine_session_create(&local_session));
+}
+
+TEST_F(VineSessionTest, DestroyN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_session_destroy(NULL));
+}
+
+TEST_F(VineSessionTest, CreateDestroyP)
+{
+ vine_session_h local_session;
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_session_create(&local_session));
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_session_destroy(local_session));
+}
+
+TEST_F(VineSessionTest, SetDiscoveryN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_session_set_discovery_method(NULL, VINE_DISCOVERY_METHOD_DNS_SD));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_session_set_discovery_method(session, (vine_discovery_method_e)-1));
+}
+
+TEST_F(VineSessionTest, SetDiscoveryP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_session_set_discovery_method(session, VINE_DISCOVERY_METHOD_DNS_SD));
+}
+
+TEST_F(VineSessionTest, RegisterN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_session_register(NULL, NULL, NULL));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_session_register(session, NULL, NULL));
+
+ vine_session_register(session, service, NULL);
+ EXPECT_EQ(VINE_ERROR_INVALID_OPERATION,
+ vine_session_register(session, service, NULL));
+ vine_session_unregister(session);
+}
+
+TEST_F(VineSessionTest, UnregisterN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_session_unregister(NULL));
+
+ EXPECT_EQ(VINE_ERROR_INVALID_OPERATION,
+ vine_session_unregister(session));
+
+ vine_session_start_discovery(session, SERVICE_TYPE, NULL);
+ EXPECT_EQ(VINE_ERROR_INVALID_OPERATION,
+ vine_session_unregister(session));
+}
+
+TEST_F(VineSessionTest, StartDiscoveryN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_session_start_discovery(NULL, NULL, NULL));
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_session_start_discovery(session, NULL, NULL));
+
+ char long_type[100] = {0, };
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_session_start_discovery(session, long_type, NULL));
+
+ vine_session_start_discovery(session, SERVICE_TYPE, NULL);
+ EXPECT_EQ(VINE_ERROR_INVALID_OPERATION,
+ vine_session_start_discovery(session, SERVICE_TYPE, NULL));
+ vine_session_stop_discovery(session);
+}
+
+TEST_F(VineSessionTest, StopDiscoveryN)
+{
+ EXPECT_EQ(VINE_ERROR_INVALID_PARAMETER,
+ vine_session_stop_discovery(NULL));
+
+ EXPECT_EQ(VINE_ERROR_INVALID_OPERATION,
+ vine_session_stop_discovery(session));
+
+ vine_session_register(session, service, NULL);
+ EXPECT_EQ(VINE_ERROR_INVALID_OPERATION,
+ vine_session_stop_discovery(session));
+ vine_session_unregister(session);
+}
+
+TEST_F(VineSessionTest, StartStopDiscoveryP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_session_start_discovery(session, SERVICE_TYPE, NULL));
+ EXPECT_EQ(VINE_ERROR_NONE,
+ vine_session_stop_discovery(session));
+}
+
+TEST_F(VineSessionTest, GetFdP)
+{
+ int fd = 0;
+ EXPECT_EQ(VINE_ERROR_NONE, vine_session_get_event_fd(session, &fd));
+ EXPECT_GT(fd, 0);
+}
+
+/* There is no way to test vine_process_event
+TEST_F(VineSessionTest, ProcessEventP)
+{
+ EXPECT_EQ(VINE_ERROR_NONE, vine_process_event(session));
+}
+*/
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <map>
+#include <string>
+
+#include <net/if.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "vine.h"
+#include "vine-constants.h"
+#include "vine-unittest-utils.h"
+
+typedef struct {
+ int type;
+ char service_type[VINE_MAX_SERVICE_TYPE_LEN + 1];
+ char service_name[VINE_MAX_SERVICE_NAME_LEN + 1];
+ char host_name[VINE_MAX_HOST_NAME_LEN + 1];
+ char ip[VINE_MAX_IP_LEN + 1];
+ vine_address_family_e family;
+ int port;
+ std::map<std::string, std::string> attributes;
+ char iface_name[IF_NAMESIZE + 1];
+ vine_service_state_e state;
+
+ void *disc_handle;
+ vine_session_ip_resolved_cb ip_resolved_cb;
+ void *ip_resolved_cb_data;
+} __dummy_service_s;
+
+vine_service_h _create_dummy_service()
+{
+ __dummy_service_s *s = new __dummy_service_s;
+ s->type = 0;
+ strncpy(s->service_type, SERVICE_TYPE, VINE_MAX_SERVICE_TYPE_LEN);
+ strncpy(s->service_name, SERVICE_NAME, VINE_MAX_SERVICE_NAME_LEN);
+ s->port = PORT;
+ strncpy(s->iface_name, "vine0", IF_NAMESIZE);
+
+ return (vine_service_h)s;
+}
+
+void _destroy_dummy_service(vine_service_h service)
+{
+ free(service);
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <vine.h>
+
+#define SERVICE_TYPE "VINE_TYPE"
+#define SERVICE_NAME "VINE_SERVICE"
+#define KEY "Key"
+#define KEY2 "Key2"
+#define VALUE "Value"
+#define PORT 1234
+
+vine_service_h _create_dummy_service();
+void _destroy_dummy_service(vine_service_h service);
+
+vine_security_h _create_dummy_security();
+void _destroy_dummy_security(vine_security_h security);
--- /dev/null
+# Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+SET(VINE_VERIFIER "vine-verifier")
+LINK_DIRECTORIES(${CMAKE_BINARY_DIR})
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -Wall -fPIE")
+SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -pie")
+
+INCLUDE_DIRECTORIES(
+ ${CMAKE_SOURCE_DIR}/include
+ ${VINE_LOGGER_PATH}
+ ${fw_name_deps_INCLUDE_DIRS}
+ )
+
+FILE(GLOB VINE_VERIFIER_SRCS *.cpp)
+ADD_EXECUTABLE(${VINE_VERIFIER} ${VINE_VERIFIER_SRCS})
+TARGET_LINK_LIBRARIES(${VINE_VERIFIER}
+ ${TARGET_VINE}
+ ${VINE_LOGGER}
+ ${fw_name_deps_LIBRARIES}
+ )
+
+IF(ANDROID)
+SET(CERTS_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/usr/share/vine-verifier")
+ELSE(ANDROID)
+SET(CERTS_INSTALL_PREFIX "/usr/share/vine-verifier")
+ENDIF(ANDROID)
+INSTALL(FILES certs/rootCA/ca.pem DESTINATION "${CERTS_INSTALL_PREFIX}/certs/rootCA")
+INSTALL(FILES certs/server/test-server.pem DESTINATION "${CERTS_INSTALL_PREFIX}/certs/server")
+INSTALL(FILES certs/server/test-server.key DESTINATION "${CERTS_INSTALL_PREFIX}/certs/server")
+INSTALL(TARGETS ${VINE_VERIFIER} DESTINATION "${BIN_DIR}")
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIC9TCCAd2gAwIBAgIUMLWz1S0J2T3+2FJHiD/b7CTaJTAwDQYJKoZIhvcNAQEL
+BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIxMDMxNjAyMjk0MFoXDTI0MDEw
+NDAyMjk0MFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAnxbMmTZddJuPLBDcBHb3aMPz6blTrnBCiLr67dYKMLXM
+Sd6eAB/I89PMBpbFIvK9etNcN/K88vRufqEc75r4fHuueOQ2AnGgCoTlxFzuZQ7t
+2uBvQ5+bOlyHao+bTbfiSGJlG2jDdWqg3S6wp25Hbc6rVf3cvon9eWjAX0DTvuAT
+JoFne0J8K94wMFyPYxQjfIqMc3u/Xc6oSIVpr9U62tCLNM57hT77Gq58vyAgqJoL
+3kkAvEdT0fgNYOfKfU8U2ZMyKOz1NYDXwFzkjkCYLYmH/IlquBoRXTrU6AA+HMXf
+lOWzxAsKDvS8jrkR33N7R7GhkhmcXinp2lBDRHQ1CQIDAQABoz8wPTAPBgNVHRMB
+Af8EBTADAQH/MAsGA1UdDwQEAwICBDAdBgNVHQ4EFgQUR6/jvQX4Vcq4yHpB+sBi
+aUkF91wwDQYJKoZIhvcNAQELBQADggEBAHHw/bR2KahILeXj1Ptof0urm71tRyPZ
+56dvy9N5X6FzAslb+nYa9JOGSgv8O5ts60UnFD4sOAXC6h06Ek0UynZqkzZKWabb
+M1YCjANs3PD3k+A3gj3o5+oQWlh4xLrt4cZMzfftKGfEO6qUSACt/OR4ldTB0mDH
+tCjrvOp6jJRmaS7aGO39v4pmzFX8wM2+4Iil4mvssaepwFit7X7SkMTjSzKPXWJc
+4dmzHf/bJBHu251fmmDvwzcarqUyy093Uj6ukS7tUmuFNtuyRwaLOGuJ3mPn+t5n
+C8nzI25krt5zL/FqCSq3Y//3KyLn/msNgKdRFmU/2uwUl8P4cRHhUmM=
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAk94UgCbQw/58MzFY7AGp+DpS0DJhqMnnpqdLwe8mryhY+GRM
+shL6GrNiQ4KzPlHiSCYHxsvjHnYz/xBnIU8F9iPkI71NQ45HW7Br6gQVnsipBybm
+NFt1qzKJjJDCHhViE1F87/ZnjXRBR0daXS94fT4H5Ei9BOnSyWr7OwGFE6gGwHFC
+3AUynJEN7ZV1mPXkLD0FzNu8ZTJhjf9Ly+kcchMowvjOJQ5Yi2qDGF6c5MiZWf54
+GMydsi5Z2M7rdcLECTHFiOLq+uG+Ott18+Ic49SKs6N0wYqi5wWAKW4+PdCArurf
+dlcEpDP1v0xwrIXHsOKDih+4Hm3LqxFQigBjHQIDAQABAoIBAAYl9MpDUujjWvyx
+75WYmB2nJyxa++6NvqESUbcVn5Em51Qkr3+0BEw56zsNYzCTLQp88lPUxHPOzGit
+oOwkogXTBjhqNZ06fEMWudX4J4H7q/ONLYM9zk25AzkiB8BCeVp+R+ieYT3jeJdj
+IyG+yI7EloVqdNmQc2BGn32bmZHwmXEZJ1JslpuezOAQWO3WGkB4lEHfumIHl+xM
+dnRXSuHYOhNfiDs4k4r75prL02K8PjfC1Fkg35QNY0RtwDQii4HCUJdkDZywnD55
+d7aTDPxXODRs4vQ9yMv2Lb14u9au541It1Lz/ZfDicQ2TCZZkYbMmtooX6dKVuwa
+B2rKWqECgYEAxDHLx3Tw0cmocKj3OiRtevBTFa5kSaIlGCtQWdWEyi9uLqc55MHy
+oGlpG6VR9R4HfSvx63S17OBXnj9bOrr3IPurmpcf1+7BT2H9fZZNsKIecTkm1Ms3
+PQH2cMiDz8KmQeWDpv5eO+Qnah6ippbUrtUItsaAUOJW6nQIWz4UhekCgYEAwPEL
+d38jT/z1KAmZiSFje3/aFjfVRnFSb0FkABKZYRc7S+DJAD0sLot2x61xWOdjW3+W
+u230ncF+3T+7l8BEk+Yti3QY2Is2nDbu3NXyCItYSo0UT3B6kcp5hxjoziv+WdM+
+A/wHY2garmkiwamuem7cvrHYih6vbSbJfyF6zxUCgYEAtYUI8Cp+4Op94HZD/tRT
+Qqp1hJrMCOBvB5STi/okGNiXTazEwKS88bN1XvGvCWVWMnZlJp7d7yKFjlE5+/Wd
+zjNrSuVFvggVbHfSC87zZFgqaEEjbz/xhI17UFAEvEVwg4lxLCEuWKU33lQn0o+8
+iWFq5Yh0keFH3zWpwZmHbXkCgYBkaI0pIPcFl4UV0vkeDkwxZzXdviy1vh/CWmtq
+RkOf7XEkadTw1OT7TAUCoVaNh82DPoD5BZ35w0r8ZUCBFsQZm/4zxrVva/N9lHD2
+aDjPDYVjNsxNEyIWYSKhlesB8I8ru+1YMX6+0tyfy4MU+fMdMDic3Pzt1E5DJZ5T
+FN9OiQKBgALb2ZnIhjDo9NiOtJGQbrpAReDTKbtvtzxafjtxQcO6FgKOumr0RWnV
+v2djNeCQCiYkj7aMjSI0hrBtQbIvJzbf8hCAt9wMptK4ZrkyXEDTqG1Pwg7dtcso
+6OmSqr5UGw4n7kWq7qGFgSAX1h661xAslLX/bUajgvUoN/ciKwaF
+-----END RSA PRIVATE KEY-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDATCCAemgAwIBAgIEAINoFTANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls
+b2NhbGhvc3QwHhcNMjEwMzE2MDIyOTQwWhcNMjQwMTA0MDIyOTQwWjAUMRIwEAYD
+VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCT
+3hSAJtDD/nwzMVjsAan4OlLQMmGoyeemp0vB7yavKFj4ZEyyEvoas2JDgrM+UeJI
+JgfGy+MedjP/EGchTwX2I+QjvU1DjkdbsGvqBBWeyKkHJuY0W3WrMomMkMIeFWIT
+UXzv9meNdEFHR1pdL3h9PgfkSL0E6dLJavs7AYUTqAbAcULcBTKckQ3tlXWY9eQs
+PQXM27xlMmGN/0vL6RxyEyjC+M4lDliLaoMYXpzkyJlZ/ngYzJ2yLlnYzut1wsQJ
+McWI4ur64b4623Xz4hzj1Iqzo3TBiqLnBYApbj490ICu6t92VwSkM/W/THCshcew
+4oOKH7gebcurEVCKAGMdAgMBAAGjWzBZMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYE
+FFFL10hPYuHhvcwoI8JXVKI35heLMB8GA1UdIwQYMBaAFEev470F+FXKuMh6QfrA
+YmlJBfdcMAkGA1UdEgQCMAAwDQYJKoZIhvcNAQELBQADggEBAGyZ+vLjzg/5AQ1X
+BX5akgHl4SPWv6pHHdF8ngSbT5Uqui0Cam/gy2V+WRY3WAAtde8tM6QpgTXFbTae
+qomlM8IwbwG+gB3nHr8WuPCA3/A2b67fDR3GsCDFgeyTPBwggU7hxud1MbFkdGkR
+/7H/LfyPwERfWfBHyNJ+JKlo6FXf9QsZiezW63dd4peVU3f2cihSvvdQIHyZdN9O
+CU9YYl4zNCoKjth2jD8FsOoNKxegJbLX0IAUfccoX8yTc+IcqLwEO8u0R6lF4D4t
+GMICcXeu+iVR85TAOTkhEG8Cp6cIPIN4wDjE0v5Xjbu8Ibh9i+W2fQCSoqwfR0g1
+KcxKMwU=
+-----END CERTIFICATE-----
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <list>
+#include <iterator>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <sys/epoll.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <time.h>
+#include <vine.h>
+#include <vine-log.h>
+
+#define MAX_EVENTS 16
+
+#define TEST_DEBUG
+
+#define RESET_COLOR "\e[m"
+#define MAKE_RED "\e[31m"
+#define MAKE_GREEN "\e[32m"
+
+#define PRINT_IF_ERROR(ret, func) __print_result(ret, func, false)
+#define PRINT_RESULT(ret, func) __print_result(ret, func, true)
+
+#define SERVICE_TYPE "VineVerifier"
+#define SERVICE_NAME "VineTizenTestApp"
+#define PORT 12345
+#define SERVER_MSG "SERVER HELLO"
+#define CLIENT_MSG "CLIENT HELLO"
+
+#define TOPIC "VinePubSubTopic"
+#define PUBSUB_MSG "PUBSUB HELLO"
+
+#define PSK "ABCDEFG1234567"
+
+#define ROOT_CA_PATH "/usr/share/vine-verifier/certs/rootCA/ca.pem"
+#define SERVER_CERT_PATH "/usr/share/vine-verifier/certs/server/test-server.pem"
+#define SERVER_PRIVATE_KEY_PATH "/usr/share/vine-verifier/certs/server/test-server.key"
+
+int g_epollfd = 0;
+
+static int is_dp_running = false;
+
+static const char *__error_to_str(vine_error_e error)
+{
+ switch (error) {
+ case VINE_ERROR_NONE:
+ return "NO ERROR";
+ case VINE_ERROR_NOT_PERMITTED:
+ return "NOT PERMITTED";
+ case VINE_ERROR_OUT_OF_MEMORY:
+ return "OUT OF MEMORY";
+ case VINE_ERROR_PERMISSION_DENIED:
+ return "PERMISSION DENIED";
+ case VINE_ERROR_INVALID_PARAMETER:
+ return "INVALID PARAMETER";
+ case VINE_ERROR_INVALID_OPERATION:
+ return "INVALID OPERATION";
+ case VINE_ERROR_CONNECTION_TIME_OUT:
+ return "CONNECTION TIME OUT";
+ case VINE_ERROR_NOW_IN_PROGRESS:
+ return "NOW IN PROGRESS";
+ case VINE_ERROR_NOT_SUPPORTED:
+ return "NOT SUPPORTED";
+ case VINE_ERROR_NOT_INITIALIZED:
+ return "NOT INITIALIZED";
+ case VINE_ERROR_ALREADY_INITIALIZED:
+ return "ALREADY INITIALIZED";
+ case VINE_ERROR_ALREADY_ENABLED:
+ return "ALREADY ENABLED";
+ case VINE_ERROR_OPERATION_FAILED:
+ return "OPERATION FAILED";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static void __print_error(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+
+ printf(MAKE_RED);
+ vprintf(format, args);
+ printf(RESET_COLOR"\n");
+
+ va_end(args);
+}
+
+static void __print_result(int ret, const char *function_name, bool print_if_no_error)
+{
+ if (ret == VINE_ERROR_NONE) {
+ if (print_if_no_error)
+ printf(MAKE_GREEN "%s" RESET_COLOR "\n", function_name);
+ } else {
+ __print_error("%s: %s(%d) ", function_name, __error_to_str((vine_error_e)ret), ret);
+ }
+}
+
+static void __event_handler(vine_session_h session)
+{
+ PRINT_IF_ERROR(vine_session_process_event(session), "vine_process_event");
+}
+
+static void __start_event_loop(vine_session_h session)
+{
+ int fd;
+ int ret = vine_session_get_event_fd(session, &fd);
+ if (ret != VINE_ERROR_NONE) {
+ PRINT_IF_ERROR(ret, "vine_get_event_fd");
+ return;
+ }
+
+ struct epoll_event ev;
+ ev.events = EPOLLIN;
+ ev.data.ptr = session;
+ if (epoll_ctl(g_epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+ __print_error("Fail to add an epoll event for fd %d: %d", fd, errno);
+ exit(1);
+ }
+}
+
+static void __init()
+{
+ PRINT_IF_ERROR(vine_initialize(), "vine_initialize()");
+}
+
+static vine_security_h __get_vine_security(bool use_tls, bool use_psk)
+{
+ printf("Set security..\n");
+ vine_security_type_e type = VINE_SECURITY_TYPE_NONE;
+ if (use_tls) {
+ if (use_psk)
+ type = VINE_SECURITY_TYPE_PSK_OVER_TLS;
+ else
+ type = VINE_SECURITY_TYPE_TLS;
+ }
+ vine_security_h security;
+ if (vine_security_create(&security) != VINE_ERROR_NONE)
+ return NULL;
+ if (vine_security_set_type(security, (vine_security_type_e)type) != VINE_ERROR_NONE) {
+ vine_security_destroy(security);
+ return NULL;
+ }
+ if (type >= VINE_SECURITY_TYPE_TLS) {
+ vine_security_set_ca_path(security, ROOT_CA_PATH);
+ vine_security_set_cert_path(security, SERVER_CERT_PATH);
+ vine_security_set_verification_flags(security,
+ VINE_SECURITY_VERIFICATION_FLAG_ALLOW_SELF_SIGNED
+ | VINE_SECURITY_VERIFICATION_FLAG_SKIP_HOST_NAME_CHECK);
+// vine_security_set_tls_version(security, VINE_SECURITY_TLS_VERSION_1_3);
+ vine_security_set_private_key(security, SERVER_PRIVATE_KEY_PATH);
+ }
+ if (type == VINE_SECURITY_TYPE_PSK_OVER_TLS) {
+ vine_security_set_psk(security, PSK);
+ }
+
+ return security;
+}
+
+static void __print_received_data(unsigned char *buf, size_t len)
+{
+ for (int i = 0; i < (int)len; i++)
+ printf("%c", buf[i]);
+ printf("\n");
+}
+
+static void __terminated_cb(vine_dp_h dp, void *user_data)
+{
+ printf(MAKE_GREEN "[TERMINATED_CB] %p is terminated." RESET_COLOR "\n", dp);
+ vine_dp_destroy(dp);
+}
+
+static void __server_received_cb(vine_dp_h dp, size_t received_len, void *user_data)
+{
+ unsigned char buf[1024] = {0, };
+ size_t bytes = 0;
+ printf(MAKE_GREEN "[RECV_CB] %p received %zd bytes." RESET_COLOR "\n", dp, received_len);
+ do {
+ vine_dp_recv(dp, buf, 1024, &bytes);
+ __print_received_data(buf, bytes);
+ received_len -= bytes;
+ } while (received_len > 0);
+}
+
+static void __accepted_cb(vine_dp_h dp, vine_dp_h accepted_dp, void *user_data)
+{
+ printf("Accept a connection\n");
+
+ vine_dp_set_received_cb(accepted_dp, __server_received_cb, NULL);
+ vine_dp_set_terminated_cb(accepted_dp, __terminated_cb, NULL);
+
+ printf("Send a message to the client\n");
+ vine_dp_send(accepted_dp, (unsigned char *)SERVER_MSG, strlen(SERVER_MSG));
+}
+
+static void __registered_cb(vine_session_h session,
+ const char *service_name, vine_error_e error, void *user_data)
+{
+ if (error == VINE_ERROR_NONE) {
+ PRINT_RESULT(error, "Successfully registered");
+ printf("Service Name: %s\n", service_name);
+ } else if (error == VINE_ERROR_NAME_CONFLICT) {
+ PRINT_RESULT(error, "Successfully registered, but service name conflicted");
+ printf("Service Name: %s\n", service_name);
+ } else {
+ PRINT_IF_ERROR(error, "Failed to register a service");
+ }
+}
+
+static void __register_service(vine_session_h session, int port)
+{
+ vine_service_h service;
+ int ret = vine_service_create(&service);
+ if (ret != VINE_ERROR_NONE) {
+ PRINT_IF_ERROR(ret, "vine_service_create");
+ return;
+ }
+
+ PRINT_RESULT(vine_service_set_type(service, SERVICE_TYPE),
+ "vine_service_set_type");
+ PRINT_RESULT(vine_service_set_name(service, SERVICE_NAME),
+ "vine_service_set_name");
+ PRINT_RESULT(vine_service_add_attribute(service, "KEY1", "VALUE1"),
+ "vine_service_add_attribute");
+ PRINT_RESULT(vine_service_set_port(service, port),
+ "vine_service_set_port");
+
+ vine_session_set_registered_cb(session, __registered_cb, NULL);
+ PRINT_IF_ERROR(vine_session_register(session, service, NULL), "vine_session_register");
+}
+
+static void __server_opened_cb(vine_dp_h dp, vine_error_e result, void *user_data)
+{
+ if (result == VINE_ERROR_NONE) {
+ vine_session_h session = (vine_session_h)user_data;
+ printf("Success to open a server\n");
+ printf("Start listening..\n");
+ int port;
+ vine_dp_get_port(dp, &port);
+ __register_service(session, port);
+ } else {
+ PRINT_IF_ERROR(result, "Fail to open a server.");
+ }
+}
+
+static void __start_server(bool use_tls, bool use_psk)
+{
+ printf("Start server\n");
+ printf("Create a session..\n");
+ vine_session_h session;
+ int ret;
+ ret = vine_session_create(&session);
+ if (ret != VINE_ERROR_NONE) {
+ PRINT_IF_ERROR(ret, "vine_session_create");
+ return;
+ }
+
+ __start_event_loop(session);
+
+ vine_security_h security = __get_vine_security(use_tls, use_psk);
+
+ vine_dp_h dp;
+ vine_dp_create(session, VINE_DP_TYPE_SERVER, &dp);
+ vine_dp_set_accepted_cb(dp, __accepted_cb, NULL);
+ vine_dp_set_port(dp, PORT);
+ vine_dp_set_security(dp, security);
+ vine_security_destroy(security);
+ PRINT_RESULT(vine_dp_open(dp, __server_opened_cb, session), "vine_dp_open");
+}
+
+static void __client_opened_cb(vine_dp_h dp, vine_error_e result, void *user_data)
+{
+ if (result == VINE_ERROR_NONE)
+ printf("Success to connect a server\n");
+ else
+ PRINT_IF_ERROR(result, "Fail to connect to the server.");
+}
+
+static void __client_received_cb(vine_dp_h dp, size_t received_len, void *user_data)
+{
+ unsigned char buf[1024] = {0, };
+ size_t bytes = 0;
+ printf(MAKE_GREEN "[RECV_CB] %p received %zd bytes." RESET_COLOR "\n", dp, received_len);
+ do {
+ vine_dp_recv(dp, buf, 1024, &bytes);
+ __print_received_data(buf, bytes);
+ received_len -= bytes;
+ } while (received_len > 0);
+
+ printf("Send a message to the server\n");
+ vine_dp_send(dp, (unsigned char *)CLIENT_MSG, strlen(CLIENT_MSG));
+}
+
+static void __connect_to_server(vine_session_h session, bool use_tls, bool use_psk,
+ const char *ip, vine_address_family_e address_family, int port)
+{
+ vine_security_h security = __get_vine_security(use_tls, use_psk);
+
+ printf("Create a client data path..\n");
+ printf("AddressFamily: %d, IP: %s\n", address_family, ip);
+
+ vine_dp_h dp;
+ vine_dp_create(session, VINE_DP_TYPE_CLIENT, &dp);
+ vine_dp_set_remote_ip(dp, address_family, ip);
+ vine_dp_set_port(dp, port);
+ vine_dp_set_security(dp, security);
+
+ vine_dp_set_received_cb(dp, __client_received_cb, NULL);
+ vine_dp_open(dp, __client_opened_cb, NULL);
+ vine_security_destroy(security);
+}
+
+struct connect_info {
+ bool use_tls;
+ bool use_psk;
+};
+
+static void __ip_resolved_cb(vine_session_h session, vine_service_h service,
+ const char *ip, vine_address_family_e address_family, /* availability */
+ void *user_data)
+{
+ // NOTE: Ignore a link-local address temporary.
+ // This should be handled by dns-sd-plugin.
+ if (!is_dp_running && strncmp(ip, "fe80:", 5) != 0) {
+ is_dp_running = true;
+ vine_session_unset_ip_resolved_cb(session, service);
+ struct connect_info * info = (struct connect_info *)user_data;
+ int port;
+ int ret = vine_service_get_port(service, &port);
+ PRINT_IF_ERROR(ret, "vine_service_get_port");
+
+ __connect_to_server(session, info->use_tls, info->use_psk, ip, address_family, port);
+ free(info);
+ }
+}
+
+static bool __print_attr(const char *key, const char *value, void *user_data)
+{
+ printf(" > Attributes: %s=%s\n", key, value);
+ return true;
+}
+
+static void __discovered_cb(vine_session_h session, vine_service_h service,
+ vine_service_state_e state, void *user_data)
+{
+ int ret;
+ char *service_type;
+ char *service_name;
+ int port;
+
+ vine_session_unset_discovered_cb(session);
+
+ ret = vine_service_get_type(service, &service_type);
+ PRINT_IF_ERROR(ret, "vine_service_get_type");
+ ret = vine_service_get_name(service, &service_name);
+ PRINT_IF_ERROR(ret, "vine_service_get_name");
+ ret = vine_service_get_port(service, &port);
+ PRINT_IF_ERROR(ret, "vine_service_get_port");
+
+ if (state == VINE_SERVICE_AVAILABLE) {
+ printf("Find a service");
+ printf(" > Service Type: %s\n", service_type);
+ printf(" > Service Name: %s\n", service_name);
+ printf(" > Port: %d\n", port);
+ ret = vine_service_foreach_attribute(service, __print_attr, NULL);
+ PRINT_IF_ERROR(ret, "vine_service_foreach_attribute");
+ printf("\n");
+ fflush(stdout);
+
+ ret = vine_session_set_ip_resolved_cb(session, service, __ip_resolved_cb, user_data);
+ PRINT_IF_ERROR(ret, "vine_session_set_ip_resolved_cb");
+ }
+}
+
+static void __start_client(bool use_tls, bool use_psk)
+{
+ printf("Start client\n");
+ printf("Create a session..\n");
+ vine_session_h session;
+ int ret;
+ ret = vine_session_create(&session);
+ if (ret != VINE_ERROR_NONE) {
+ PRINT_IF_ERROR(ret, "vine_session_create");
+ return;
+ }
+
+ __start_event_loop(session);
+
+ struct connect_info *info = (struct connect_info *)calloc(1, sizeof(struct connect_info));
+ info->use_tls = use_tls;
+ info->use_psk = use_psk;
+ vine_session_set_discovered_cb(session, __discovered_cb, info);
+ vine_session_start_discovery(session, SERVICE_TYPE, NULL);
+}
+
+static void __pubsub_opened_cb(vine_dp_h dp, vine_error_e result, void *user_data)
+{
+ if (result == VINE_ERROR_NONE) {
+ printf("Success to open a pub/sub data path\n");
+ vine_dp_send(dp, (unsigned char *)PUBSUB_MSG, strlen(PUBSUB_MSG));
+ } else {
+ PRINT_IF_ERROR(result, "Fail to open the pub/sub data path.");
+ }
+}
+
+static void __pubsub_received_cb(vine_dp_h dp, size_t received_len, void *user_data)
+{
+ unsigned char buf[1024] = {0, };
+ size_t bytes = 0;
+ printf(MAKE_GREEN "[RECV_CB] %p received %zd bytes." RESET_COLOR "\n", dp, received_len);
+ do {
+ vine_dp_recv(dp, buf, 1024, &bytes);
+ __print_received_data(buf, bytes);
+ received_len -= bytes;
+ } while (received_len > 0);
+}
+
+static void __start_pubsub(bool use_tls, bool use_psk)
+{
+ printf("Start pub/sub\n");
+ printf("Create a session..\n");
+ vine_session_h session;
+ int ret;
+ ret = vine_session_create(&session);
+ if (ret != VINE_ERROR_NONE) {
+ PRINT_IF_ERROR(ret, "vine_session_create");
+ return;
+ }
+
+ __start_event_loop(session);
+
+ vine_security_h security = __get_vine_security(use_tls, use_psk);
+
+ vine_dp_h dp;
+ vine_dp_create(session, VINE_DP_TYPE_PUBSUB, &dp);
+ vine_dp_set_address_family(session, VINE_ADDRESS_FAMILY_IPV4);
+ vine_dp_set_security(session, security);
+ vine_dp_set_topic(session, TOPIC);
+ vine_dp_set_received_cb(dp, __pubsub_received_cb, NULL);
+ PRINT_RESULT(vine_dp_open(dp, __pubsub_opened_cb, session), "vine_dp_open");
+ vine_security_destroy(security);
+}
+
+enum {
+ CMD_SERVER_OPEN,
+ CMD_SERVER_TLS,
+ CMD_SERVER_TLS_PSK,
+ CMD_CLIENT_OPEN,
+ CMD_CLIENT_TLS,
+ CMD_CLIENT_TLS_PSK,
+ CMD_PUBSUB_OPEN,
+ CMD_PUBSUB_TLS,
+ CMD_PUBSUB_TLS_PSK,
+ CMD_INVALID,
+};
+
+static struct {
+ const char *type;
+ void (*func)(bool, bool);
+ bool use_tls;
+ bool use_psk;
+} __menus[] = {
+ [CMD_SERVER_OPEN] = {"Server", __start_server, false, false},
+ [CMD_SERVER_TLS] = {"Server", __start_server, true, false},
+ [CMD_SERVER_TLS_PSK] = {"Server", __start_server, true, true},
+ [CMD_CLIENT_OPEN] = {"Client", __start_client, false, false},
+ [CMD_CLIENT_TLS] = {"Client", __start_client, true, false},
+ [CMD_CLIENT_TLS_PSK] = {"Client", __start_client, true, true},
+ [CMD_PUBSUB_OPEN] = {"PubSub", __start_pubsub, false, false},
+ [CMD_PUBSUB_TLS] = {"PubSub", __start_pubsub, true, false},
+ [CMD_PUBSUB_TLS_PSK] = {"PubSub", __start_pubsub, true, true},
+ {NULL, NULL, false, false},
+};
+
+static void __show_menus()
+{
+ for (int i = 0; __menus[i].type; ++i) {
+ if (__menus[i].use_tls) {
+ if (__menus[i].use_psk)
+ printf("%02d %s (Security: TLS + PSK)\n", i, __menus[i].type);
+ else
+ printf("%02d %s (Security: TLS)\n", i, __menus[i].type);
+ } else {
+ printf("%02d %s (Security: OPEN)\n", i, __menus[i].type);
+ }
+ }
+ printf("> ");
+ fflush(stdout);
+}
+
+static inline int __is_digit(const char* str)
+{
+ int len;
+ int i;
+
+ if (str == NULL)
+ return -1;
+
+ if (strlen(str) == 0)
+ return -1;
+
+ len = strlen(str);
+ for (i = 0; i < len; i++) {
+ if (str[i] < '0' || str[i] > '9')
+ return -2;
+ }
+
+ return 0;
+}
+
+static void __process_input(const char *input)
+{
+ int cmd = -1;
+
+ cmd = strtol(input, NULL, 0);
+ if (__is_digit(input) < 0 || strlen(input) == 0 || errno == ERANGE || errno
+ == EINVAL)
+ cmd = CMD_INVALID;
+
+ if (cmd >= CMD_INVALID || cmd < 0) {
+ printf("Invalid CMD\n");
+ return;
+ }
+ __menus[cmd].func(__menus[cmd].use_tls, __menus[cmd].use_psk);
+}
+
+static void __terminal_read_std_input()
+{
+ int fd = 0;
+
+ static char buf[1024];
+ int n;
+
+ errno = 0;
+ n = read(fd, buf, 1024);
+ if (n == 0) {
+ printf("Error: read() from stdin returns 0.\n");
+ } else if (n < 0) {
+ printf("input: read, err\n");
+ } else if (n - 1 > 0 && n < 1024) {
+ buf[n - 1] = '\0'; /* remove new line... */
+ printf("\n\n");
+ __process_input(buf);
+ } else {
+ if (buf[0] == '\n' || buf[0] == '\r')
+ __show_menus();
+ else
+ printf("invalid input\n");
+ }
+}
+
+int main()
+{
+ __init();
+ __show_menus();
+
+ g_epollfd = epoll_create1(0);
+ if (g_epollfd == -1) {
+ __print_error("Fail to create epoll fd %d", errno);
+ return -1;
+ }
+
+ struct epoll_event ev, events[MAX_EVENTS];
+ ev.events = EPOLLIN;
+ ev.data.fd = STDIN_FILENO;
+ if (epoll_ctl(g_epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) == -1) {
+ __print_error("Fail to add an epoll event for stdin: %d", errno);
+ return -1;
+ }
+
+ while (true) {
+ int n = epoll_wait(g_epollfd, events, MAX_EVENTS, 0);
+ if (n == -1) {
+ __print_error("epoll_wait %d", errno);
+ return -1;
+ }
+
+ for (int i = 0; i < n; ++i) {
+ if (events[i].data.fd == STDIN_FILENO) {
+ __terminal_read_std_input();
+ } else {
+ __event_handler(events[i].data.ptr);
+ }
+ }
+ }
+ close(g_epollfd);
+
+ return 0;
+}
--- /dev/null
+# Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+SET(VINE_TEST "vine-test")
+LINK_DIRECTORIES(${CMAKE_BINARY_DIR})
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -Wall -fPIE")
+SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -pie")
+
+INCLUDE_DIRECTORIES(
+ ${CMAKE_SOURCE_DIR}/include
+ ${VINE_LOGGER_PATH}
+ ${fw_name_deps_INCLUDE_DIRS}
+)
+
+FILE(GLOB VINE_TEST_SRCS *.cpp)
+foreach(file ${VINE_TEST_SRCS})
+ GET_FILENAME_COMPONENT(name ${file} NAME_WE)
+ ADD_EXECUTABLE(${name} ${file})
+ TARGET_LINK_LIBRARIES(${name}
+ ${TARGET_VINE}
+ ${VINE_LOGGER}
+ ${fw_name_deps_LIBRARIES}
+ )
+ INSTALL(TARGETS ${name} DESTINATION "${BIN_DIR}")
+endforeach()
+
--- /dev/null
+#!/bin/bash
+PWD=`pwd -P`
+CONF_PATH=$PWD"/custom-cert.cnf"
+ROOT_CA_DIR="rootCA/"
+ROOT_CA_KEY="ca.key"
+ROOT_CA_CERT="ca.pem"
+
+SERVER_DIR="server/"
+SERVER_SIGN_REQUEST="test-server.csr"
+SERVER_KEY="test-server.key"
+SERVER_CERT="test-server.pem"
+
+# Create directories and necessary files.
+mkdir $SERVER_DIR
+mkdir -p $ROOT_CA_DIR"newcerts"
+
+touch $ROOT_CA_DIR/serial
+SERIAL=`shuf -i 100000-999999 -n 1`
+echo $SERIAL > $ROOT_CA_DIR/serial
+touch $ROOT_CA_DIR/db.txt
+touch $ROOT_CA_DIR/db.txt.attr
+
+# Create Root CA Certificate
+echo "============ Create Root CA ============="
+cd $ROOT_CA_DIR
+openssl genrsa -out $ROOT_CA_KEY 2048
+openssl req -config $CONF_PATH -x509 -new -nodes -key $ROOT_CA_KEY -sha256 -days 1024 -out $ROOT_CA_CERT
+
+# Create Server Certificate
+echo "======= Create Server certificate ======="
+cd ..
+openssl genrsa -out $SERVER_DIR$SERVER_KEY 2048
+openssl req -config $CONF_PATH \
+ -new -key $SERVER_DIR$SERVER_KEY \
+ -out $SERVER_DIR$SERVER_SIGN_REQUEST
+openssl ca -config $CONF_PATH \
+ -keyfile $ROOT_CA_DIR$ROOT_CA_KEY \
+ -cert $ROOT_CA_DIR$ROOT_CA_CERT \
+ -days 1024 -notext -md sha256 \
+ -in $SERVER_DIR$SERVER_SIGN_REQUEST \
+ -out $SERVER_DIR$SERVER_CERT
+echo "Finished."
--- /dev/null
+####################################################################
+# Custom Root CA configuration file.
+####################################################################
+[ req ]
+default_bits = 2048
+default_keyfile = ./rootCA/ca.key
+default_md = sha256
+distinguished_name = req_DN
+string_mask = utf8only
+x509_extensions = v3_selfsign
+
+[ req_DN ]
+commonName = "localhost"
+commonName_value = "localhost"
+
+[ v3_selfsign ]
+basicConstraints = critical,CA:true
+keyUsage = keyCertSign
+subjectKeyIdentifier=hash
+
+####################################################################
+[ ca ]
+default_ca = CA_default
+
+####################################################################
+[ CA_default ]
+dir = ./rootCA
+certificate = ./rootCA/cacert.pem
+serial = ./rootCA/serial
+database =./rootCA/db.txt
+private_key = ./rootCA/private/cakey.pem
+new_certs_dir = ./rootCA/newcerts
+
+certificate = cacert.pem
+private_key = cakey.pem
+
+x509_extensions = v3_user
+
+name_opt = ca_default # Subject Name options
+cert_opt = ca_default # Certificate field options
+
+policy = policy_anything
+
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ v3_user ]
+basicConstraints=critical,CA:FALSE
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+issuerAltName=issuer:copy
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/epoll.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <time.h>
+#include <vine.h>
+#include <vine-log.h>
+#include <pthread.h>
+
+#define KGRN "\033[0;32;32m"
+//#define KCYN "\033[0;36m"
+#define KRED "\033[0;32;31m"
+#define KYEL "\033[1;33m"
+//#define KMAG "\033[0;35m"
+//#define KBLU "\033[0;32;34m"
+#define KCYN_L "\033[1;36m"
+#define RESET "\033[0m"
+
+
+#define PRINT_IF_ERROR(ret, func) __print_result(ret, func, false)
+#define PRINT_RESULT(ret, func) __print_result(ret, func, true)
+#define PRINT_LOG(format, args...) __print_log("[TID:%u] " format, (unsigned int)syscall(__NR_gettid), ##args)
+
+#define MAX_EVENTS 10
+
+#define MAX_SERVICE_TYPE_LEN 63
+#define MAX_SERVICE_NAME_LEN 63
+#define MAX_IP_LEN 39 // for IPv6
+
+static const char *__error_to_str(vine_error_e error)
+{
+ switch (error) {
+ case VINE_ERROR_NONE:
+ return "NO ERROR";
+ case VINE_ERROR_NOT_PERMITTED:
+ return "NOT PERMITTED";
+ case VINE_ERROR_OUT_OF_MEMORY:
+ return "OUT OF MEMORY";
+ case VINE_ERROR_PERMISSION_DENIED:
+ return "PERMISSION DENIED";
+ case VINE_ERROR_INVALID_PARAMETER:
+ return "INVALID PARAMETER";
+ case VINE_ERROR_INVALID_OPERATION:
+ return "INVALID OPERATION";
+ case VINE_ERROR_CONNECTION_TIME_OUT:
+ return "CONNECTION TIME OUT";
+ case VINE_ERROR_NOW_IN_PROGRESS:
+ return "NOW IN PROGRESS";
+ case VINE_ERROR_NOT_SUPPORTED:
+ return "NOT SUPPORTED";
+ case VINE_ERROR_NOT_INITIALIZED:
+ return "NOT INITIALIZED";
+ case VINE_ERROR_ALREADY_INITIALIZED:
+ return "ALREADY INITIALIZED";
+ case VINE_ERROR_ALREADY_ENABLED:
+ return "ALREADY ENABLED";
+ case VINE_ERROR_OPERATION_FAILED:
+ return "OPERATION FAILED";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static void __print_error(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+
+ printf(KRED);
+ vprintf(format, args);
+ printf(RESET"\n");
+
+ va_end(args);
+}
+
+static void __print_log(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+
+ printf(KGRN);
+ vprintf(format, args);
+ printf(RESET"\n");
+
+ va_end(args);
+}
+
+static void __print_result(int ret, const char *function_name, bool print_if_no_error)
+{
+ unsigned int tid = (unsigned int)syscall(__NR_gettid);
+ if (ret == VINE_ERROR_NONE) {
+ if (print_if_no_error)
+ printf(KGRN "[TID:%u] %s" RESET "\n", tid, function_name);
+ } else {
+ __print_error("[TID:%u] %s: %s(%d) ", tid, function_name,
+ __error_to_str((vine_error_e)ret), ret);
+ }
+}
+
+static bool __print_attr(const char *key, const char *value, void *user_data)
+{
+ PRINT_LOG("\t > Attributes: %s=%s\n", key, value);
+ return true;
+}
+
+static void __discovered_cb(vine_session_h session, vine_service_h service,
+ vine_service_state_e state, void *user_data)
+{
+ PRINT_LOG("\t++ Service Discovered");
+
+ int ret;
+ char *service_type;
+ char *service_name;
+ int port;
+
+ ret = vine_service_get_type(service, &service_type);
+ PRINT_IF_ERROR(ret, "vine_service_get_type");
+ ret = vine_service_get_name(service, &service_name);
+ PRINT_IF_ERROR(ret, "vine_service_get_name");
+ ret = vine_service_get_port(service, &port);
+ PRINT_IF_ERROR(ret, "vine_service_get_port");
+
+ PRINT_LOG("\t > State: %s\n",
+ state == VINE_SERVICE_UNAVAILABLE ? "Unavailable" : "Available");
+ PRINT_LOG("\t > Service Type: %s\n", service_type);
+ PRINT_LOG("\t > Service Name: %s\n", service_name);
+ PRINT_LOG("\t > Port: %d\n", port);
+ ret = vine_service_foreach_attribute(service, __print_attr, NULL);
+ PRINT_IF_ERROR(ret, "vine_service_foreach_attribute");
+ PRINT_LOG("\n");
+ fflush(stdout);
+}
+
+#define VINE_LOGGER_TIME_MESSAGE_LEN 17
+static void __get_current_time(char *buf)
+{
+ int hour, min, sec, day, month;
+ time_t now;
+ struct tm *local;
+
+ time(&now);
+ local = localtime(&now);
+
+ hour = local->tm_hour;
+ min = local->tm_min;
+ sec = local->tm_sec;
+ day = local->tm_mday;
+ month = local->tm_mon + 1;
+
+ snprintf(buf, VINE_LOGGER_TIME_MESSAGE_LEN,
+ "[%02u-%02u %02u:%02u:%02u]", month, day, hour, min, sec);
+}
+
+static unsigned int g_main_tid = 0;
+
+void __logger(int log_level, const char *log)
+{
+ char timestamp[VINE_LOGGER_TIME_MESSAGE_LEN];
+ __get_current_time(timestamp);
+ unsigned int ctid = (unsigned int)syscall(__NR_gettid);
+
+ if (ctid == g_main_tid) {
+ printf("%s" KGRN "[T%5u]", timestamp, g_main_tid);
+ } else if (ctid == g_main_tid + 1) {
+ printf("%s" KCYN_L "[T%5u]", timestamp, ctid);
+ } else if (ctid == g_main_tid + 2) {
+ printf("%s" KYEL "[T%5u]", timestamp, ctid);
+ } else {
+ printf("%s[T%5u] ", timestamp, ctid);
+ }
+
+ switch (log_level) {
+ case VINE_LOG_DEBUG:
+ printf("D/");
+ break;
+ case VINE_LOG_INFO:
+ printf("I/");
+ break;
+ case VINE_LOG_ERROR:
+ printf(KRED "E/");
+ break;
+ }
+ printf("%s" RESET "\n", log);
+}
+
+static void __event_handler(vine_session_h session)
+{
+ PRINT_LOG("Vine Event");
+ PRINT_IF_ERROR(vine_session_process_event(session), "vine_process_event");
+}
+
+static void __start_event_loop(vine_session_h session)
+{
+ int fd;
+ int ret = vine_session_get_event_fd(session, &fd);
+ if (ret != VINE_ERROR_NONE) {
+ PRINT_IF_ERROR(ret, "vine_get_event_fd");
+ return;
+ }
+
+ int epollfd = epoll_create1(0);
+ if (epollfd == -1) {
+ __print_error("Fail to create epoll fd %d", errno);
+ exit(1);
+ }
+
+ struct epoll_event ev, events[MAX_EVENTS];
+ ev.events = EPOLLIN;
+ ev.data.fd = fd;
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+ __print_error("Fail to add an epoll event for fd %d: %d", fd, errno);
+ exit(1);
+ }
+
+ while (true) {
+ int n = epoll_wait(epollfd, events, MAX_EVENTS, 0);
+ if (n == -1) {
+ __print_error("epoll_wait %d", errno);
+ exit(1);
+ }
+
+ for (int i = 0; i < n; ++i)
+ __event_handler(session);
+ }
+ close(epollfd);
+}
+
+static void start_vine()
+{
+ PRINT_LOG("Start");
+
+ vine_session_h session;
+
+ PRINT_IF_ERROR(vine_initialize(), "vine_initialize()");
+
+ PRINT_IF_ERROR(vine_session_create(&session), "vine_session_create");
+ PRINT_IF_ERROR(vine_session_set_discovered_cb(session, __discovered_cb, NULL),
+ "vine_session_set_discovered_cb");
+ PRINT_LOG("Session Created");
+
+ PRINT_IF_ERROR(vine_session_start_discovery(session, "", NULL),
+ "vine_session_start_discovery");
+ PRINT_LOG("Subscribed");
+
+ __start_event_loop(session);
+}
+
+static void *run_thread(void *arg) {
+ start_vine();
+ return NULL;
+}
+
+int main()
+{
+ g_main_tid = (unsigned int)syscall(__NR_gettid);
+
+ pthread_t thread;
+
+ vine_set_logger(__logger);
+ vine_set_log_level(VINE_LOG_DEBUG | VINE_LOG_INFO | VINE_LOG_ERROR);
+
+ if (pthread_create(&thread, NULL, run_thread, NULL) != 0) {
+ printf("Fail to create thread\n");
+ }
+ start_vine();
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <list>
+#include <iterator>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <sys/epoll.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <time.h>
+#include <vine.h>
+#include <vine-log.h>
+
+#define MAX_EVENTS 16
+
+#define TEST_DEBUG
+
+#define RESET_COLOR "\e[m"
+#define MAKE_RED "\e[31m"
+#define MAKE_GREEN "\e[32m"
+
+#define PRINT_IF_ERROR(ret, func) __print_result(ret, func, false)
+#define PRINT_RESULT(ret, func) __print_result(ret, func, true)
+#define CHECK_SESSION \
+ do { \
+ if (!g_session) { \
+ __print_error("No Session"); \
+ return; \
+ } \
+ } while (0)
+
+#define MAX_SERVICE_TYPE_LEN 16
+#define MAX_SERVICE_NAME_LEN 63
+#define MAX_IP_LEN 39 // for IPv6
+
+#define ROOT_CA_PATH "certs/rootCA/ca.pem"
+#define SERVER_CERT_PATH "certs/server/test-server.pem"
+#define SERVER_PRIVATE_KEY_PATH "certs/server/test-server.key"
+
+int g_epollfd = 0;
+static vine_session_h g_session = NULL;
+static vine_service_h g_service = NULL;
+static vine_dp_h g_server_dp = NULL;
+static vine_dp_h g_client_dp = NULL;
+static vine_dp_h g_pubsub_dp = NULL;
+static std::list<vine_dp_h> g_datapath_list;
+static std::list<vine_service_h> g_service_list;
+
+static const char *__error_to_str(vine_error_e error)
+{
+ switch (error) {
+ case VINE_ERROR_NONE:
+ return "NO ERROR";
+ case VINE_ERROR_NOT_PERMITTED:
+ return "NOT PERMITTED";
+ case VINE_ERROR_OUT_OF_MEMORY:
+ return "OUT OF MEMORY";
+ case VINE_ERROR_PERMISSION_DENIED:
+ return "PERMISSION DENIED";
+ case VINE_ERROR_INVALID_PARAMETER:
+ return "INVALID PARAMETER";
+ case VINE_ERROR_INVALID_OPERATION:
+ return "INVALID OPERATION";
+ case VINE_ERROR_CONNECTION_TIME_OUT:
+ return "CONNECTION TIME OUT";
+ case VINE_ERROR_NOW_IN_PROGRESS:
+ return "NOW IN PROGRESS";
+ case VINE_ERROR_NOT_SUPPORTED:
+ return "NOT SUPPORTED";
+ case VINE_ERROR_NOT_INITIALIZED:
+ return "NOT INITIALIZED";
+ case VINE_ERROR_ALREADY_INITIALIZED:
+ return "ALREADY INITIALIZED";
+ case VINE_ERROR_ALREADY_ENABLED:
+ return "ALREADY ENABLED";
+ case VINE_ERROR_OPERATION_FAILED:
+ return "OPERATION FAILED";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static void __print_error(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+
+ printf(MAKE_RED);
+ vprintf(format, args);
+ printf(RESET_COLOR"\n");
+
+ va_end(args);
+}
+
+static void __print_result(int ret, const char *function_name, bool print_if_no_error)
+{
+ if (ret == VINE_ERROR_NONE) {
+ if (print_if_no_error)
+ printf(MAKE_GREEN "%s" RESET_COLOR "\n", function_name);
+ } else {
+ __print_error("%s: %s(%d) ", function_name, __error_to_str((vine_error_e)ret), ret);
+ }
+}
+
+static bool test_get_user_string(const char *msg, char *buf, int buf_size)
+{
+ if (msg == NULL || buf == NULL || buf_size < 2)
+ return false;
+
+ int ret;
+ printf("%s", msg);
+ fflush(stdout);
+ memset(buf, 0, buf_size);
+ ret = read(0, buf, buf_size - 1);
+
+ if (ret < 0 || buf[0] == '\0' || buf[0] == '\n' || buf[0] == '\r') {
+ buf[0] = '\0';
+ return false;
+ }
+
+ buf[ret - 1] = '\0';
+ return true;
+}
+
+static void __quit()
+{
+ PRINT_IF_ERROR(vine_deinitialize(), "vine_initialize()");
+ close(g_epollfd);
+ exit(1);
+}
+
+static void __event_handler(vine_session_h session)
+{
+ printf("Vine Event\n");
+ PRINT_IF_ERROR(vine_session_process_event(session), "vine_process_event");
+}
+
+static void __start_event_loop()
+{
+ int fd;
+ int ret = vine_session_get_event_fd(g_session, &fd);
+ if (ret != VINE_ERROR_NONE) {
+ PRINT_IF_ERROR(ret, "vine_get_event_fd");
+ return;
+ }
+
+ struct epoll_event ev;
+ ev.events = EPOLLIN;
+ ev.data.ptr = g_session;
+ if (epoll_ctl(g_epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+ __print_error("Fail to add an epoll event for fd %d: %d", fd, errno);
+ exit(1);
+ }
+}
+
+static void __init()
+{
+ PRINT_IF_ERROR(vine_initialize(), "vine_initialize()");
+}
+
+static void __registered_cb(vine_session_h session,
+ const char *service_name, vine_error_e error, void *user_data)
+{
+ if (error == VINE_ERROR_NONE) {
+ PRINT_RESULT(error, "Successfully registered");
+ printf("Service Name: %s\n", service_name);
+ } else if (error == VINE_ERROR_NAME_CONFLICT) {
+ PRINT_RESULT(error, "Successfully registered, but service name conflicted");
+ printf("Service Name: %s\n", service_name);
+ } else {
+ PRINT_IF_ERROR(error, "Failed to register a service");
+ }
+}
+
+static bool __print_attr(const char *key, const char *value, void *user_data)
+{
+ printf("\t > Attributes: %s=%s\n", key, value);
+ return true;
+}
+
+static void __ip_resolved_cb(vine_session_h session, vine_service_h service,
+ const char *ip, vine_address_family_e address_family, /* availability */
+ void *user_data)
+{
+ printf("\t++ IP resolved\n");
+ int ret;
+ char *service_type;
+ char *service_name;
+ int port;
+
+ ret = vine_service_get_type(service, &service_type);
+ PRINT_IF_ERROR(ret, "vine_service_get_type");
+ ret = vine_service_get_name(service, &service_name);
+ PRINT_IF_ERROR(ret, "vine_service_get_name");
+ ret = vine_service_get_port(service, &port);
+ PRINT_IF_ERROR(ret, "vine_service_get_port");
+
+ printf("\t > Service Type: %s\n", service_type);
+ printf("\t > Service Name: %s\n", service_name);
+ printf("\t > IP Address: %s\n", ip);
+ printf("\t > Port: %d\n", port);
+ ret = vine_service_foreach_attribute(service, __print_attr, NULL);
+ PRINT_IF_ERROR(ret, "vine_service_foreach_attribute");
+ printf("\n");
+ fflush(stdout);
+}
+
+static void __discovered_cb(vine_session_h session, vine_service_h service,
+ vine_service_state_e state, void *user_data)
+{
+ printf("\t++ Service Discovered\n");
+
+ int ret;
+ char *service_type;
+ char *service_name;
+ int port;
+
+ ret = vine_service_get_type(service, &service_type);
+ PRINT_IF_ERROR(ret, "vine_service_get_type");
+ ret = vine_service_get_name(service, &service_name);
+ PRINT_IF_ERROR(ret, "vine_service_get_name");
+ ret = vine_service_get_port(service, &port);
+ PRINT_IF_ERROR(ret, "vine_service_get_port");
+
+ printf("\t > State: %s\n",
+ state == VINE_SERVICE_UNAVAILABLE ? "Unavailable" : "Available");
+ printf("\t > Service Type: %s\n", service_type);
+ printf("\t > Service Name: %s\n", service_name);
+ printf("\t > Port: %d\n", port);
+ ret = vine_service_foreach_attribute(service, __print_attr, NULL);
+ PRINT_IF_ERROR(ret, "vine_service_foreach_attribute");
+ printf("\n");
+ fflush(stdout);
+
+ g_service_list.push_back(service);
+}
+
+static void __print_received_data(unsigned char *buf, size_t len)
+{
+ for (int i = 0; i < (int)len; i++)
+ printf("%c", buf[i]);
+ printf("\n");
+}
+
+static void __received_cb(vine_dp_h dp, size_t received_len, void *user_data)
+{
+ unsigned char buf[20] = {0, };
+ size_t bytes = 0;
+
+ printf(MAKE_GREEN "[RECV_CB] %p received %zd bytes." RESET_COLOR "\n", dp, received_len);
+ do {
+ vine_dp_recv(dp, buf, 20, &bytes);
+ __print_received_data(buf, bytes);
+ received_len -= bytes;
+ } while (received_len > 0);
+}
+
+static void __terminated_cb(vine_dp_h dp, void *user_data)
+{
+ printf(MAKE_GREEN "[TERMINATED_CB] %p is terminated." RESET_COLOR "\n", dp);
+ g_datapath_list.remove(dp);
+ vine_dp_destroy(dp);
+}
+
+static void __add_new_dp(vine_dp_h dp)
+{
+ g_datapath_list.push_back(dp);
+ vine_dp_set_received_cb(dp, __received_cb, NULL);
+ vine_dp_set_terminated_cb(dp, __terminated_cb, NULL);
+}
+
+static void __opened_cb(vine_dp_h dp, vine_error_e result, void *user_data)
+{
+ int port;
+
+ vine_dp_get_port(dp, &port);
+ printf(MAKE_GREEN "[OPENED_CB] %p is %s. port[%d]" RESET_COLOR "\n",
+ dp, (result == VINE_ERROR_NONE) ? "opened" : "not opened", port);
+ if (dp == g_client_dp || dp == g_pubsub_dp)
+ __add_new_dp(dp);
+}
+
+static void __set_callbacks()
+{
+ CHECK_SESSION;
+ PRINT_IF_ERROR(vine_session_set_registered_cb(g_session, __registered_cb, NULL),
+ "vine_session_set_registered_cb");
+ PRINT_IF_ERROR(vine_session_set_discovered_cb(g_session, __discovered_cb, NULL),
+ "vine_session_set_discovered_cb");
+}
+
+static void __create_session()
+{
+ int ret;
+ ret = vine_session_create(&g_session);
+ if (ret != VINE_ERROR_NONE) {
+ PRINT_IF_ERROR(ret, "vine_session_create");
+ return;
+ }
+
+ __set_callbacks();
+ __start_event_loop();
+ printf("Session Created\n");
+}
+
+static void __destroy_session()
+{
+ CHECK_SESSION;
+ PRINT_IF_ERROR(vine_session_destroy(g_session),
+ "vine_session_destroy");
+}
+
+static void __set_service_type()
+{
+ CHECK_SESSION;
+ char type[64];
+ printf(" >> Service Type: ");
+ if (scanf(" %63s", type) < 1) {
+ __print_error("Scan failed");
+ return;
+ }
+ PRINT_RESULT(vine_service_set_type(g_service, type),
+ "vine_service_set_type");
+}
+
+static void __set_service_name()
+{
+ CHECK_SESSION;
+ char name[64];
+ printf(" >> Service Name (Max length 64): ");
+ if (scanf(" %63s", name) < 1) {
+ __print_error("Scan failed");
+ return;
+ }
+ PRINT_RESULT(vine_service_set_name(g_service, name),
+ "vine_service_set_name");
+}
+
+static void __set_port()
+{
+ CHECK_SESSION;
+ int port;
+ printf(" >> Port Number (0 ~ 65535): ");
+ if (scanf(" %d", &port) < 1) {
+ __print_error("Scan failed");
+ return;
+ }
+ PRINT_RESULT(vine_service_set_port(g_service, port), "vine_session_set_port");
+}
+
+static void __add_attribute()
+{
+ CHECK_SESSION;
+
+ while (true) {
+ char key[10];
+ char value[256] = {0, };
+ if (!test_get_user_string(" >> Key (Max length 9. Enter if no attribute): ", key, 10))
+ break;
+ test_get_user_string(" >> Value (Enter if empty): ", value, 256);
+ PRINT_RESULT(vine_service_add_attribute(g_service, key, value),
+ "vine_service_add_attribute");
+ }
+}
+
+static void __create_service()
+{
+ int ret;
+ ret = vine_service_create(&g_service);
+ if (ret != VINE_ERROR_NONE) {
+ PRINT_IF_ERROR(ret, "vine_service_create");
+ return;
+ }
+
+ __set_service_type();
+ __set_service_name();
+ __set_port();
+ __add_attribute();
+
+ printf("Service Created\n");
+}
+
+static void __destroy_service()
+{
+ CHECK_SESSION;
+ PRINT_IF_ERROR(vine_service_destroy(g_service), "vine_service_destroy");
+}
+
+static void __register()
+{
+ CHECK_SESSION;
+ PRINT_IF_ERROR(vine_session_register(g_session, g_service, NULL), "vine_session_register");
+ printf("Registered\n");
+}
+
+static void __unregister()
+{
+ CHECK_SESSION;
+ PRINT_IF_ERROR(vine_session_unregister(g_session), "vine_session_unregister");
+ printf("Unregistered\n");
+}
+
+static void __start_discovery()
+{
+ CHECK_SESSION;
+ char type[20];
+ printf(" >> Service Type: ");
+ if (scanf(" %19s", type) < 1) {
+ __print_error("Scan failed");
+ return;
+ }
+
+ PRINT_IF_ERROR(vine_session_start_discovery(g_session, type, NULL),
+ "vine_session_start_discovery");
+ printf("Discovery started\n");
+}
+
+static void __stop_discovery()
+{
+ CHECK_SESSION;
+ PRINT_IF_ERROR(vine_session_stop_discovery(g_session), "vine_session_stop_discovery");
+ printf("Discovery stopped\n");
+}
+
+static void __print_service_list()
+{
+ int idx = -1;
+ int ret = VINE_ERROR_NONE;
+ for (const auto &service : g_service_list) {
+ char *service_type;
+ char *service_name;
+ int port;
+
+ ret = vine_service_get_type(service, &service_type);
+ PRINT_IF_ERROR(ret, "vine_service_get_type");
+ ret = vine_service_get_name(service, &service_name);
+ PRINT_IF_ERROR(ret, "vine_service_get_name");
+ ret = vine_service_get_port(service, &port);
+ PRINT_IF_ERROR(ret, "vine_service_get_port");
+ printf("\t %d: type[%s] name[%s] port[%d]\n", ++idx, service_type, service_name, port);
+ free(service_type);
+ free(service_name);
+ }
+}
+
+static vine_service_h __get_service()
+{
+ __print_service_list();
+
+ int idx;
+ printf(" >> Select service: \n");
+ if (scanf("%d", &idx) < 0)
+ return NULL;
+
+ auto it = g_service_list.begin();
+ std::advance(it, idx);
+
+ return (vine_service_h) *it;
+}
+
+static void __resolve_ip()
+{
+ CHECK_SESSION;
+ int ret = vine_session_set_ip_resolved_cb(g_session, __get_service(), __ip_resolved_cb, NULL);
+ PRINT_IF_ERROR(ret, "vine_session_set_ip_resolved_cb");
+}
+
+static void __stop_resolve_ip()
+{
+ CHECK_SESSION;
+ int ret = vine_session_unset_ip_resolved_cb(g_session, __get_service());
+ PRINT_IF_ERROR(ret, "vine_session_unset_ip_resolved_cb");
+}
+
+#ifdef TEST_DEBUG
+#define VINE_LOGGER_TIME_MESSAGE_LEN 17
+static void __get_current_time(char *buf)
+{
+ int hour, min, sec, day, month;
+ time_t now;
+ struct tm *local;
+
+ time(&now);
+ local = localtime(&now);
+
+ hour = local->tm_hour;
+ min = local->tm_min;
+ sec = local->tm_sec;
+ day = local->tm_mday;
+ month = local->tm_mon + 1;
+
+ snprintf(buf, VINE_LOGGER_TIME_MESSAGE_LEN,
+ "[%02u-%02u %02u:%02u:%02u]", month, day, hour, min, sec);
+}
+
+void __logger(int log_level, const char *log)
+{
+ char timestamp[VINE_LOGGER_TIME_MESSAGE_LEN];
+ __get_current_time(timestamp);
+ printf("%s[T%5u] ", timestamp, (unsigned int)syscall(__NR_gettid));
+
+ switch (log_level) {
+ case VINE_LOG_DEBUG:
+ printf("D/");
+ break;
+ case VINE_LOG_INFO:
+ printf("I/");
+ break;
+ case VINE_LOG_ERROR:
+ printf(MAKE_RED "E/");
+ break;
+ }
+ printf("%s" RESET_COLOR "\n", log);
+}
+#endif
+
+static void __debug_on_off()
+{
+ static int __debug_on = 0;
+ __debug_on = 1 - __debug_on;
+ printf("Debug %s\n", __debug_on ? "ON" : "OFF");
+#ifdef TEST_DEBUG
+ if (__debug_on) {
+ vine_set_logger(__logger);
+ vine_set_log_level(VINE_LOG_DEBUG | VINE_LOG_INFO | VINE_LOG_ERROR);
+ } else {
+ vine_set_logger(NULL);
+ }
+#endif
+}
+
+static vine_security_h __create_security_handle(int type, bool is_server)
+{
+ vine_security_h s = NULL;
+
+ if (type == VINE_SECURITY_TYPE_NONE)
+ return NULL;
+ if (vine_security_create(&s) != VINE_ERROR_NONE)
+ return NULL;
+
+ if (vine_security_set_type(s, (vine_security_type_e)type) != VINE_ERROR_NONE)
+ return NULL;
+
+ if (type >= VINE_SECURITY_TYPE_TLS) {
+ vine_security_set_ca_path(s, ROOT_CA_PATH);
+ vine_security_set_cert_path(s, SERVER_CERT_PATH);
+ vine_security_set_verification_flags(s, VINE_SECURITY_VERIFICATION_FLAG_ALLOW_SELF_SIGNED | VINE_SECURITY_VERIFICATION_FLAG_SKIP_HOST_NAME_CHECK);
+ vine_security_set_tls_version(s, VINE_SECURITY_TLS_VERSION_1_3);
+ if (is_server)
+ vine_security_set_private_key(s, SERVER_PRIVATE_KEY_PATH);
+ }
+
+ if (type == VINE_SECURITY_TYPE_PSK_OVER_TLS) {
+ char psk[256] = {0, };
+ while (!test_get_user_string(" >> PSK (Max length 256): ", psk, 256)) {
+ }
+ vine_security_set_psk(s, psk);
+ }
+
+ return s;
+}
+
+static void __accepted_cb(vine_dp_h dp, vine_dp_h accepted_dp, void *user_data)
+{
+ printf(MAKE_GREEN "[ACCEPTED_CB] %p is accepted." RESET_COLOR "\n", accepted_dp);
+ __add_new_dp(accepted_dp);
+}
+
+static void __open_server()
+{
+ CHECK_SESSION;
+ int addr_family = 0;
+ int port = 0;
+ int security_type = 0;
+
+ printf(" >> Address type(0-default, 1-IPv4, 2-IPv6): ");
+ if (scanf(" %d", &addr_family) < 1) {
+ __print_error("Scan failed");
+ return;
+ }
+
+ printf(" >> listen port: ");
+ if (scanf(" %d", &port) < 1) {
+ __print_error("Scan failed");
+ return;
+ }
+
+ printf("Set security type(0.NONE, 1.TLS, 2.PSK): ");
+ if (scanf("%d", &security_type) < 0) {
+ __print_error("Scan failed");
+ return;
+ }
+
+ vine_dp_create(g_session, VINE_DP_TYPE_SERVER, &g_server_dp);
+ vine_dp_set_accepted_cb(g_server_dp, __accepted_cb, NULL);
+ vine_dp_set_terminated_cb(g_server_dp, __terminated_cb, NULL);
+ vine_dp_set_address_family(g_server_dp, (vine_address_family_e)addr_family);
+ vine_dp_set_port(g_server_dp, port);
+ vine_dp_set_security(g_server_dp, __create_security_handle(security_type, true));
+
+ PRINT_RESULT(vine_dp_open(g_server_dp, __opened_cb, NULL), "vine_dp_open");
+}
+
+static void __connect_server()
+{
+ CHECK_SESSION;
+ int addr_type;
+ char ip[40] = {0, };
+ int port = 0;
+ int security_type = 0;
+
+ printf(" >> Address Family(0-IPv4, 1-IPv6): ");
+ if (scanf(" %d", &addr_type) < 1) {
+ __print_error("Scan failed");
+ return;
+ }
+ printf(" >> Peer IP: ");
+ if (scanf(" %39s", ip) < 1) {
+ __print_error("Scan failed");
+ return;
+ }
+
+ printf(" >> Peer port: ");
+ if (scanf(" %d", &port) < 1) {
+ __print_error("Scan failed");
+ return;
+ }
+
+ printf("Set security type(0.NONE, 1.TLS, 2.PSK): ");
+ if (scanf("%d", &security_type) < 0) {
+ __print_error("Scan failed");
+ return;
+ }
+
+ vine_dp_create(g_session, VINE_DP_TYPE_CLIENT, &g_client_dp);
+ vine_dp_set_remote_ip(g_client_dp,
+ addr_type ? VINE_ADDRESS_FAMILY_IPV6 : VINE_ADDRESS_FAMILY_IPV4, ip);
+ PRINT_RESULT(vine_dp_set_port(g_client_dp, port), "vine_dp_set_port");
+ vine_dp_set_security(g_client_dp, __create_security_handle(security_type, false));
+ PRINT_RESULT(vine_dp_open(g_client_dp, __opened_cb, NULL), "vine_dp_open");
+}
+
+static void __join_service()
+{
+ CHECK_SESSION;
+ int port = 0;
+ int addr_family = 0;
+ char topic[64];
+
+ printf(" >> Address type(0-default, 1-IPv4, 2-IPv6): ");
+ if (scanf(" %d", &addr_family) < 1) {
+ __print_error("Scan failed");
+ return;
+ }
+
+ printf(" >> Listen port: ");
+ if (scanf(" %d", &port) < 1) {
+ __print_error("Scan failed");
+ return;
+ }
+
+ printf(" >> Set topic: ");
+ if (scanf(" %63s", topic) < 1) {
+ __print_error("Scan failed");
+ return;
+ }
+
+ vine_dp_create(g_session, VINE_DP_TYPE_PUBSUB, &g_pubsub_dp);
+ vine_dp_set_address_family(g_pubsub_dp, (vine_address_family_e)addr_family);
+ vine_dp_set_port(g_pubsub_dp, port);
+ vine_dp_set_topic(g_pubsub_dp, topic);
+ // TODO: set security.
+
+ vine_dp_set_terminated_cb(g_pubsub_dp, __terminated_cb, NULL);
+ PRINT_RESULT(vine_dp_open(g_pubsub_dp, __opened_cb, NULL), "vine_dp_open");
+}
+
+void __print_datapath_list()
+{
+ int idx = -1;
+ for (const auto &dp : g_datapath_list)
+ printf("\t %d: datapath[%p]\n", ++idx, dp);
+}
+
+static void __send_data()
+{
+ CHECK_SESSION;
+
+ const char *test_str[3] = {
+ "12345678910",
+ "Hello! This is a test string.",
+ "Tizen is an open and flexible operating system" \
+ "built from the ground up to address the needs of all stakeholders of the mobile"\
+ "and connected device ecosystem, including device manufacturers, mobile operators,"\
+ "application developers and independent software vendors (ISVs)."\
+ "Tizen is developed by a community of developers, under open source governance,"\
+ "and is open to all members who wish to participate."
+ };
+
+ __print_datapath_list();
+
+ int idx;
+ printf(" >> Select peer: \n");
+ if (scanf("%d", &idx) < 0)
+ return;
+
+ auto it = g_datapath_list.begin();
+ std::advance(it, idx);
+ vine_dp_h dp = *it;
+
+ for (int i = 0; i < 3; i++) {
+ int ret = vine_dp_send(dp,
+ (unsigned char *)test_str[i], strlen(test_str[i]));
+ PRINT_IF_ERROR(ret, "vine_data_path_write");
+ }
+}
+
+static void __close_data_path()
+{
+ __print_datapath_list();
+
+ int idx;
+ printf(" >> Select peer: \n");
+ if (scanf("%d", &idx) < 0)
+ return;
+
+ auto it = g_datapath_list.begin();
+ std::advance(it, idx);
+ vine_dp_h dp = *it;
+
+ PRINT_RESULT(vine_dp_close(dp), "vine_dp_close");
+ g_datapath_list.remove(dp);
+ PRINT_RESULT(vine_dp_destroy(dp), "vine_dp_destroy");
+}
+
+enum {
+ CMD_QUIT = 0,
+ CMD_CREATE_SESSION,
+ CMD_DESTROY_SESSION,
+ CMD_CREATE_SERVICE,
+ CMD_DESTROY_SERVICE,
+ CMD_REGISTER,
+ CMD_UNREGISTER,
+ CMD_START_DISCOVERY,
+ CMD_STOP_DISCOVERY,
+ CMD_RESOLVE_IP,
+ CMD_STOP_RESOLVING_IP,
+ CMD_OPEN_SERVER,
+ CMD_CONNECT_SERVER,
+ CMD_JOIN_SERVICE,
+ CMD_SEND_DATA,
+ CMD_CLOSE_DATA_PATH,
+ CMD_DEBUG,
+ CMD_INVALID,
+};
+
+static struct {
+ const char *str;
+ void (*func)(void);
+} __menus[] = {
+ [CMD_QUIT] = {"quit", __quit},
+ [CMD_CREATE_SESSION] = {"Create session", __create_session},
+ [CMD_DESTROY_SESSION] = {"Destroy session", __destroy_session},
+ [CMD_CREATE_SERVICE] = {"Create service", __create_service},
+ [CMD_DESTROY_SERVICE] = {"Destroy service", __destroy_service},
+ [CMD_REGISTER] = {"Register a service", __register},
+ [CMD_UNREGISTER] = {"Unregister a service", __unregister},
+ [CMD_START_DISCOVERY] = {"Start discovery", __start_discovery},
+ [CMD_STOP_DISCOVERY] = {"Stop discovery", __stop_discovery},
+ [CMD_RESOLVE_IP] = {"Resolve IP", __resolve_ip},
+ [CMD_STOP_RESOLVING_IP] = {"Stop resolveing IP", __stop_resolve_ip},
+ [CMD_OPEN_SERVER] = {"Open server", __open_server},
+ [CMD_CONNECT_SERVER] = {"Connect to server", __connect_server},
+ [CMD_JOIN_SERVICE] = {"Join a service", __join_service},
+ [CMD_SEND_DATA] = {"Send data to peer", __send_data},
+ [CMD_CLOSE_DATA_PATH] = {"Close datapath", __close_data_path},
+ [CMD_DEBUG] = {"Debug on/off", __debug_on_off},
+ {NULL, NULL},
+};
+
+static void __show_menus()
+{
+ printf("\n-- Vine Test\n");
+ for (int i = 0; __menus[i].str; ++i)
+ printf("%02d %s\n", i, __menus[i].str);
+ printf("> ");
+ fflush(stdout);
+}
+
+static inline int __is_digit(const char* str)
+{
+ int len;
+ int i;
+
+ if (str == NULL)
+ return -1;
+
+ if (strlen(str) == 0)
+ return -1;
+
+ len = strlen(str);
+ for (i = 0; i < len; i++) {
+ if (str[i] < '0' || str[i] > '9')
+ return -2;
+ }
+
+ return 0;
+}
+
+static void __process_input(const char *input)
+{
+ int cmd = -1;
+
+ cmd = strtol(input, NULL, 0);
+ if (__is_digit(input) < 0 || strlen(input) == 0 || errno == ERANGE || errno
+ == EINVAL)
+ cmd = CMD_INVALID;
+
+ if (cmd >= CMD_INVALID || cmd < CMD_QUIT) {
+ printf("Invalid CMD\n");
+ return;
+ }
+ __menus[cmd].func();
+}
+
+static void __terminal_read_std_input()
+{
+ int fd = 0;
+
+ static char buf[1024];
+ int n;
+
+ errno = 0;
+ n = read(fd, buf, 1024);
+ if (n == 0) {
+ printf("Error: read() from stdin returns 0.\n");
+ } else if (n < 0) {
+ printf("input: read, err\n");
+ } else if (n - 1 > 0 && n < 1024) {
+ buf[n - 1] = '\0'; /* remove new line... */
+ printf("\n\n");
+ __process_input(buf);
+ } else {
+ if (buf[0] == '\n' || buf[0] == '\r')
+ __show_menus();
+ else
+ printf("invalid input\n");
+ }
+}
+
+int main()
+{
+ __init();
+ __show_menus();
+
+ g_epollfd = epoll_create1(0);
+ if (g_epollfd == -1) {
+ __print_error("Fail to create epoll fd %d", errno);
+ return -1;
+ }
+
+ struct epoll_event ev, events[MAX_EVENTS];
+ ev.events = EPOLLIN;
+ ev.data.fd = STDIN_FILENO;
+ if (epoll_ctl(g_epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) == -1) {
+ __print_error("Fail to add an epoll event for stdin: %d", errno);
+ return -1;
+ }
+
+ while (true) {
+ int n = epoll_wait(g_epollfd, events, MAX_EVENTS, 0);
+ if (n == -1) {
+ __print_error("epoll_wait %d", errno);
+ return -1;
+ }
+
+ for (int i = 0; i < n; ++i) {
+ if (events[i].data.fd == STDIN_FILENO) {
+ __terminal_read_std_input();
+ } else {
+ __event_handler(events[i].data.ptr);
+ }
+ }
+ }
+ close(g_epollfd);
+
+ return 0;
+}
--- /dev/null
+### Prebuilt libraries
+
+#### Directory Hierarchy
+
+```
+├── [OS]
+│  ├── [Architecture]
+│  │  └─── <file: Static library>
+│  └── <file: Build information>
+```
+
+- OS: OS name. tizen, android and so on.
+- Architechture: x86, x86_64, armv7l, aarch64 and so on.
+- Static Library: libwebsockets.a
+- Build information: README.md
+ Opensource version information.
--- /dev/null
+### build version
+
+- Libwebsockets v4.0-stable
+- OpenSSL 1.1.1k
--- /dev/null
+/* lws_config.h Generated from lws_config.h.in */
+
+#ifndef NDEBUG
+ #ifndef _DEBUG
+ #define _DEBUG
+ #endif
+#endif
+
+#define LWS_INSTALL_DATADIR "./share"
+#define LWS_LIBRARY_VERSION_MAJOR 4
+#define LWS_LIBRARY_VERSION_MINOR 0
+#define LWS_LIBRARY_VERSION_PATCH 22
+/* LWS_LIBRARY_VERSION_NUMBER looks like 1005001 for e.g. version 1.5.1 */
+#define LWS_LIBRARY_VERSION_NUMBER (LWS_LIBRARY_VERSION_MAJOR * 1000000) + \
+ (LWS_LIBRARY_VERSION_MINOR * 1000) + \
+ LWS_LIBRARY_VERSION_PATCH
+#define LWS_MAX_SMP 1
+
+/* #undef LWS_LIBRARY_VERSION_NUMBER */
+
+/* #undef LWS_AVOID_SIGPIPE_IGN */
+#define LWS_BUILD_HASH "e87a691"
+/* #undef LWS_BUILTIN_GETIFADDRS */
+#define LWS_CLIENT_HTTP_PROXYING
+/* #undef LWS_DETECTED_PLAT_IOS */
+/* #undef LWS_FALLBACK_GETHOSTBYNAME */
+#define LWS_HAS_INTPTR_T
+#define LWS_HAS_GETOPT_LONG
+/* #undef LWS_HAVE__ATOI64 */
+#define LWS_HAVE_ATOLL
+#define LWS_HAVE_BN_bn2binpad
+#define LWS_HAVE_CLOCK_GETTIME
+#define LWS_HAVE_EC_POINT_get_affine_coordinates
+#define LWS_HAVE_ECDSA_SIG_set0
+#define LWS_HAVE_EVP_MD_CTX_free
+#define LWS_HAVE_EVP_aes_128_wrap
+#define LWS_HAVE_EVP_aes_128_cfb8
+#define LWS_HAVE_EVP_aes_128_cfb128
+#define LWS_HAVE_EVP_aes_192_cfb8
+#define LWS_HAVE_EVP_aes_192_cfb128
+#define LWS_HAVE_EVP_aes_256_cfb8
+#define LWS_HAVE_EVP_aes_256_cfb128
+#define LWS_HAVE_EVP_aes_128_xts
+#define LWS_HAVE_EXECVPE
+/* #undef LWS_HAVE_LIBCAP */
+#define LWS_HAVE_HMAC_CTX_new
+#define LWS_HAVE_MALLOC_H
+/* #undef LWS_HAVE_MALLOC_TRIM */
+#define LWS_HAVE_MALLOC_USABLE_SIZE
+/* #undef LWS_HAVE_mbedtls_net_init */
+/* #undef LWS_HAVE_mbedtls_ssl_conf_alpn_protocols */
+/* #undef LWS_HAVE_mbedtls_ssl_get_alpn_protocol */
+/* #undef LWS_HAVE_mbedtls_ssl_conf_sni */
+/* #undef LWS_HAVE_mbedtls_ssl_set_hs_ca_chain */
+/* #undef LWS_HAVE_mbedtls_ssl_set_hs_own_cert */
+/* #undef LWS_HAVE_mbedtls_ssl_set_hs_authmode */
+/* #undef LWS_HAVE_MBEDTLS_NET_SOCKETS */
+/* #undef LWS_HAVE_NEW_UV_VERSION_H */
+#define LWS_HAVE_OPENSSL_ECDH_H
+#define LWS_HAVE_PIPE2
+#define LWS_HAVE_EVENTFD
+#define LWS_HAVE_PTHREAD_H
+#define LWS_HAVE_RSA_SET0_KEY
+/* #undef LWS_HAVE_RSA_verify_pss_mgf1 */
+#define LWS_HAVE_SSL_CTX_get0_certificate
+#define LWS_HAVE_SSL_CTX_set1_param
+#define LWS_HAVE_SSL_CTX_set_ciphersuites
+#define LWS_HAVE_SSL_EXTRA_CHAIN_CERTS
+#define LWS_HAVE_SSL_get0_alpn_selected
+#define LWS_HAVE_SSL_CTX_EVP_PKEY_new_raw_private_key
+#define LWS_HAVE_SSL_set_alpn_protos
+#define LWS_HAVE_SSL_SET_INFO_CALLBACK
+/* #undef LWS_HAVE__STAT32I64 */
+#define LWS_HAVE_STDINT_H
+#define LWS_HAVE_SYS_CAPABILITY_H
+#define LWS_HAVE_TLS_CLIENT_METHOD
+#define LWS_HAVE_TLSV1_2_CLIENT_METHOD
+/* #undef LWS_HAVE_UV_VERSION_H */
+#define LWS_HAVE_VFORK
+#define LWS_HAVE_X509_get_key_usage
+#define LWS_HAVE_X509_VERIFY_PARAM_set1_host
+#define LWS_LIBRARY_VERSION "4.0.22"
+#define LWS_LOGGING_BITFIELD_CLEAR 0
+#define LWS_LOGGING_BITFIELD_SET 0
+/* #undef LWS_MINGW_SUPPORT */
+/* #undef LWS_NO_CLIENT */
+#define LWS_NO_DAEMONIZE
+#define LWS_OPENSSL_CLIENT_CERTS "../share"
+#define LWS_OPENSSL_SUPPORT
+/* #undef LWS_PLAT_OPTEE */
+#define LWS_PLAT_UNIX
+/* #undef LWS_PLAT_FREERTOS */
+/* #undef LWS_ROLE_CGI */
+/* #undef LWS_ROLE_DBUS */
+#define LWS_ROLE_H1
+#define LWS_ROLE_H2
+#define LWS_ROLE_RAW
+#define LWS_ROLE_RAW_FILE
+/* #undef LWS_ROLE_RAW_PROXY */
+#define LWS_ROLE_WS
+/* #undef LWS_ROLE_MQTT */
+/* #undef LWS_SHA1_USE_OPENSSL_NAME */
+#define LWS_SSL_CLIENT_USE_OS_CA_CERTS
+/* #undef LWS_SSL_SERVER_WITH_ECDH_CERT */
+/* #undef LWS_WITH_ABSTRACT */
+/* #undef LWS_WITH_ACCESS_LOG */
+/* #undef LWS_WITH_ACME */
+/* #undef LWS_WITH_ALSA */
+/* #undef LWS_WITH_SYS_ASYNC_DNS */
+/* #undef LWS_WITH_BORINGSSL */
+/* #undef LWS_WITH_CGI */
+#define LWS_WITH_CUSTOM_HEADERS
+/* #undef LWS_WITH_DEPRECATED_LWS_DLL */
+/* #undef LWS_WITH_DETAILED_LATENCY */
+#define LWS_WITH_DIR
+/* #undef LWS_WITH_ESP32 */
+/* #undef LWS_HAVE_EVBACKEND_LINUXAIO */
+/* #undef LWS_HAVE_EVBACKEND_IOURING */
+#define LWS_WITH_EXTERNAL_POLL
+#define LWS_WITH_FILE_OPS
+/* #undef LWS_WITH_FSMOUNT */
+/* #undef LWS_WITH_FTS */
+/* #undef LWS_WITH_GENCRYPTO */
+/* #undef LWS_WITH_GENERIC_SESSIONS */
+/* #undef LWS_WITH_GLIB */
+/* #undef LWS_WITH_GTK */
+#define LWS_WITH_HTTP2
+#define LWS_WITH_HTTP_BASIC_AUTH
+/* #undef LWS_WITH_HTTP_BROTLI */
+/* #undef LWS_WITH_HTTP_PROXY */
+/* #undef LWS_WITH_HTTP_STREAM_COMPRESSION */
+#define LWS_WITH_HTTP_UNCOMMON_HEADERS
+#define LWS_WITH_IPV6
+/* #undef LWS_WITH_JOSE */
+#define LWS_WITH_LEJP
+/* #undef LWS_WITH_LIBEV */
+/* #undef LWS_WITH_LIBEVENT */
+/* #undef LWS_WITH_LIBUV */
+#define LWS_WITH_LWSAC
+#define LWS_LOGS_TIMESTAMP
+/* #undef LWS_WITH_MBEDTLS */
+/* #undef LWS_WITH_MINIZ */
+#define LWS_WITH_NETWORK
+/* #undef LWS_WITH_NO_LOGS */
+#define LWS_WITH_CLIENT
+#define LWS_WITHOUT_EXTENSIONS
+#define LWS_WITH_SERVER
+/* #undef LWS_WITH_SPAWN */
+/* #undef LWS_WITH_PEER_LIMITS */
+/* #undef LWS_WITH_PLUGINS */
+/* #undef LWS_WITH_POLARSSL */
+#define LWS_WITH_POLL
+/* #undef LWS_WITH_RANGES */
+/* #undef LWS_WITH_SECURE_STREAMS */
+/* #undef LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM */
+/* #undef LWS_WITH_SECURE_STREAMS_PROXY_API */
+/* #undef LWS_WITH_SELFTESTS */
+#define LWS_WITH_SEQUENCER
+/* #undef LWS_WITH_SERVER_STATUS */
+/* #undef LWS_WITH_SMTP */
+/* #undef LWS_WITH_SOCKS5 */
+/* #undef LWS_WITH_STATEFUL_URLDECODE */
+/* #undef LWS_WITH_STATS */
+/* #undef LWS_WITH_STRUCT_SQLITE3 */
+/* #undef LWS_WITH_STRUCT_JSON */
+/* #undef LWS_WITH_SQLITE3 */
+/* #undef LWS_WITH_SYS_NTPCLIENT */
+/* #undef LWS_WITH_SYS_DHCP_CLIENT */
+/* #undef LWS_WITH_THREADPOOL */
+#define LWS_WITH_TLS
+#define LWS_WITH_UDP
+/* #undef LWS_WITH_UNIX_SOCK */
+/* #undef LWS_WITH_ZIP_FOPS */
+/* #undef USE_OLD_CYASSL */
+/* #undef USE_WOLFSSL */
+
+
--- /dev/null
+/* lws_config.h Generated from lws_config.h.in */
+
+#ifndef NDEBUG
+ #ifndef _DEBUG
+ #define _DEBUG
+ #endif
+#endif
+
+#define LWS_INSTALL_DATADIR "./share"
+#define LWS_LIBRARY_VERSION_MAJOR 4
+#define LWS_LIBRARY_VERSION_MINOR 0
+#define LWS_LIBRARY_VERSION_PATCH 22
+/* LWS_LIBRARY_VERSION_NUMBER looks like 1005001 for e.g. version 1.5.1 */
+#define LWS_LIBRARY_VERSION_NUMBER (LWS_LIBRARY_VERSION_MAJOR * 1000000) + \
+ (LWS_LIBRARY_VERSION_MINOR * 1000) + \
+ LWS_LIBRARY_VERSION_PATCH
+#define LWS_MAX_SMP 1
+
+/* #undef LWS_LIBRARY_VERSION_NUMBER */
+
+/* #undef LWS_AVOID_SIGPIPE_IGN */
+#define LWS_BUILD_HASH "c43e5fc"
+/* #undef LWS_BUILTIN_GETIFADDRS */
+#define LWS_CLIENT_HTTP_PROXYING
+/* #undef LWS_DETECTED_PLAT_IOS */
+/* #undef LWS_FALLBACK_GETHOSTBYNAME */
+#define LWS_HAS_INTPTR_T
+#define LWS_HAS_GETOPT_LONG
+/* #undef LWS_HAVE__ATOI64 */
+#define LWS_HAVE_ATOLL
+#define LWS_HAVE_BN_bn2binpad
+#define LWS_HAVE_CLOCK_GETTIME
+#define LWS_HAVE_EC_POINT_get_affine_coordinates
+#define LWS_HAVE_ECDSA_SIG_set0
+#define LWS_HAVE_EVP_MD_CTX_free
+#define LWS_HAVE_EVP_aes_128_wrap
+#define LWS_HAVE_EVP_aes_128_cfb8
+#define LWS_HAVE_EVP_aes_128_cfb128
+#define LWS_HAVE_EVP_aes_192_cfb8
+#define LWS_HAVE_EVP_aes_192_cfb128
+#define LWS_HAVE_EVP_aes_256_cfb8
+#define LWS_HAVE_EVP_aes_256_cfb128
+#define LWS_HAVE_EVP_aes_128_xts
+#define LWS_HAVE_EXECVPE
+/* #undef LWS_HAVE_LIBCAP */
+#define LWS_HAVE_HMAC_CTX_new
+#define LWS_HAVE_MALLOC_H
+/* #undef LWS_HAVE_MALLOC_TRIM */
+#define LWS_HAVE_MALLOC_USABLE_SIZE
+/* #undef LWS_HAVE_mbedtls_net_init */
+/* #undef LWS_HAVE_mbedtls_ssl_conf_alpn_protocols */
+/* #undef LWS_HAVE_mbedtls_ssl_get_alpn_protocol */
+/* #undef LWS_HAVE_mbedtls_ssl_conf_sni */
+/* #undef LWS_HAVE_mbedtls_ssl_set_hs_ca_chain */
+/* #undef LWS_HAVE_mbedtls_ssl_set_hs_own_cert */
+/* #undef LWS_HAVE_mbedtls_ssl_set_hs_authmode */
+/* #undef LWS_HAVE_MBEDTLS_NET_SOCKETS */
+/* #undef LWS_HAVE_NEW_UV_VERSION_H */
+#define LWS_HAVE_OPENSSL_ECDH_H
+#define LWS_HAVE_PIPE2
+#define LWS_HAVE_EVENTFD
+#define LWS_HAVE_PTHREAD_H
+#define LWS_HAVE_RSA_SET0_KEY
+/* #undef LWS_HAVE_RSA_verify_pss_mgf1 */
+#define LWS_HAVE_SSL_CTX_get0_certificate
+#define LWS_HAVE_SSL_CTX_set1_param
+#define LWS_HAVE_SSL_CTX_set_ciphersuites
+#define LWS_HAVE_SSL_EXTRA_CHAIN_CERTS
+#define LWS_HAVE_SSL_get0_alpn_selected
+#define LWS_HAVE_SSL_CTX_EVP_PKEY_new_raw_private_key
+#define LWS_HAVE_SSL_set_alpn_protos
+#define LWS_HAVE_SSL_SET_INFO_CALLBACK
+/* #undef LWS_HAVE__STAT32I64 */
+#define LWS_HAVE_STDINT_H
+#define LWS_HAVE_SYS_CAPABILITY_H
+#define LWS_HAVE_TLS_CLIENT_METHOD
+#define LWS_HAVE_TLSV1_2_CLIENT_METHOD
+/* #undef LWS_HAVE_UV_VERSION_H */
+#define LWS_HAVE_VFORK
+#define LWS_HAVE_X509_get_key_usage
+#define LWS_HAVE_X509_VERIFY_PARAM_set1_host
+#define LWS_LIBRARY_VERSION "4.0.22"
+#define LWS_LOGGING_BITFIELD_CLEAR 0
+#define LWS_LOGGING_BITFIELD_SET 0
+/* #undef LWS_MINGW_SUPPORT */
+/* #undef LWS_NO_CLIENT */
+#define LWS_NO_DAEMONIZE
+#define LWS_OPENSSL_CLIENT_CERTS "../share"
+#define LWS_OPENSSL_SUPPORT
+/* #undef LWS_PLAT_OPTEE */
+#define LWS_PLAT_UNIX
+/* #undef LWS_PLAT_FREERTOS */
+/* #undef LWS_ROLE_CGI */
+/* #undef LWS_ROLE_DBUS */
+#define LWS_ROLE_H1
+#define LWS_ROLE_H2
+#define LWS_ROLE_RAW
+#define LWS_ROLE_RAW_FILE
+/* #undef LWS_ROLE_RAW_PROXY */
+#define LWS_ROLE_WS
+/* #undef LWS_ROLE_MQTT */
+/* #undef LWS_SHA1_USE_OPENSSL_NAME */
+#define LWS_SSL_CLIENT_USE_OS_CA_CERTS
+/* #undef LWS_SSL_SERVER_WITH_ECDH_CERT */
+/* #undef LWS_WITH_ABSTRACT */
+/* #undef LWS_WITH_ACCESS_LOG */
+/* #undef LWS_WITH_ACME */
+/* #undef LWS_WITH_ALSA */
+/* #undef LWS_WITH_SYS_ASYNC_DNS */
+/* #undef LWS_WITH_BORINGSSL */
+/* #undef LWS_WITH_CGI */
+#define LWS_WITH_CUSTOM_HEADERS
+/* #undef LWS_WITH_DEPRECATED_LWS_DLL */
+/* #undef LWS_WITH_DETAILED_LATENCY */
+#define LWS_WITH_DIR
+/* #undef LWS_WITH_ESP32 */
+/* #undef LWS_HAVE_EVBACKEND_LINUXAIO */
+/* #undef LWS_HAVE_EVBACKEND_IOURING */
+#define LWS_WITH_EXTERNAL_POLL
+#define LWS_WITH_FILE_OPS
+/* #undef LWS_WITH_FSMOUNT */
+/* #undef LWS_WITH_FTS */
+/* #undef LWS_WITH_GENCRYPTO */
+/* #undef LWS_WITH_GENERIC_SESSIONS */
+/* #undef LWS_WITH_GLIB */
+/* #undef LWS_WITH_GTK */
+#define LWS_WITH_HTTP2
+#define LWS_WITH_HTTP_BASIC_AUTH
+/* #undef LWS_WITH_HTTP_BROTLI */
+/* #undef LWS_WITH_HTTP_PROXY */
+/* #undef LWS_WITH_HTTP_STREAM_COMPRESSION */
+#define LWS_WITH_HTTP_UNCOMMON_HEADERS
+#define LWS_WITH_IPV6
+/* #undef LWS_WITH_JOSE */
+#define LWS_WITH_LEJP
+/* #undef LWS_WITH_LIBEV */
+/* #undef LWS_WITH_LIBEVENT */
+/* #undef LWS_WITH_LIBUV */
+#define LWS_WITH_LWSAC
+#define LWS_LOGS_TIMESTAMP
+/* #undef LWS_WITH_MBEDTLS */
+/* #undef LWS_WITH_MINIZ */
+#define LWS_WITH_NETWORK
+/* #undef LWS_WITH_NO_LOGS */
+#define LWS_WITH_CLIENT
+#define LWS_WITHOUT_EXTENSIONS
+#define LWS_WITH_SERVER
+/* #undef LWS_WITH_SPAWN */
+/* #undef LWS_WITH_PEER_LIMITS */
+/* #undef LWS_WITH_PLUGINS */
+/* #undef LWS_WITH_POLARSSL */
+#define LWS_WITH_POLL
+/* #undef LWS_WITH_RANGES */
+/* #undef LWS_WITH_SECURE_STREAMS */
+/* #undef LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM */
+/* #undef LWS_WITH_SECURE_STREAMS_PROXY_API */
+/* #undef LWS_WITH_SELFTESTS */
+#define LWS_WITH_SEQUENCER
+/* #undef LWS_WITH_SERVER_STATUS */
+/* #undef LWS_WITH_SMTP */
+/* #undef LWS_WITH_SOCKS5 */
+/* #undef LWS_WITH_STATEFUL_URLDECODE */
+/* #undef LWS_WITH_STATS */
+/* #undef LWS_WITH_STRUCT_SQLITE3 */
+/* #undef LWS_WITH_STRUCT_JSON */
+/* #undef LWS_WITH_SQLITE3 */
+/* #undef LWS_WITH_SYS_NTPCLIENT */
+/* #undef LWS_WITH_SYS_DHCP_CLIENT */
+/* #undef LWS_WITH_THREADPOOL */
+#define LWS_WITH_TLS
+#define LWS_WITH_UDP
+/* #undef LWS_WITH_UNIX_SOCK */
+/* #undef LWS_WITH_ZIP_FOPS */
+/* #undef USE_OLD_CYASSL */
+/* #undef USE_WOLFSSL */
+
+
--- /dev/null
+### build version
+
+- Libwebsockets v4.0-stable
+- OpenSSL 1.1.1k
--- /dev/null
+/* lws_config.h Generated from lws_config.h.in */
+
+#ifndef NDEBUG
+ #ifndef _DEBUG
+ #define _DEBUG
+ #endif
+#endif
+
+#define LWS_INSTALL_DATADIR "/usr/local/share"
+#define LWS_LIBRARY_VERSION_MAJOR 4
+#define LWS_LIBRARY_VERSION_MINOR 0
+#define LWS_LIBRARY_VERSION_PATCH 22
+/* LWS_LIBRARY_VERSION_NUMBER looks like 1005001 for e.g. version 1.5.1 */
+#define LWS_LIBRARY_VERSION_NUMBER (LWS_LIBRARY_VERSION_MAJOR * 1000000) + \
+ (LWS_LIBRARY_VERSION_MINOR * 1000) + \
+ LWS_LIBRARY_VERSION_PATCH
+#define LWS_MAX_SMP 1
+
+/* #undef LWS_LIBRARY_VERSION_NUMBER */
+
+/* #undef LWS_AVOID_SIGPIPE_IGN */
+#define LWS_BUILD_HASH "d85b58c"
+/* #undef LWS_BUILTIN_GETIFADDRS */
+#define LWS_CLIENT_HTTP_PROXYING
+/* #undef LWS_DETECTED_PLAT_IOS */
+/* #undef LWS_FALLBACK_GETHOSTBYNAME */
+#define LWS_HAS_INTPTR_T
+#define LWS_HAS_GETOPT_LONG
+/* #undef LWS_HAVE__ATOI64 */
+#define LWS_HAVE_ATOLL
+#define LWS_HAVE_BN_bn2binpad
+#define LWS_HAVE_CLOCK_GETTIME
+#define LWS_HAVE_EC_POINT_get_affine_coordinates
+#define LWS_HAVE_ECDSA_SIG_set0
+#define LWS_HAVE_EVP_MD_CTX_free
+#define LWS_HAVE_EVP_aes_128_wrap
+#define LWS_HAVE_EVP_aes_128_cfb8
+#define LWS_HAVE_EVP_aes_128_cfb128
+#define LWS_HAVE_EVP_aes_192_cfb8
+#define LWS_HAVE_EVP_aes_192_cfb128
+#define LWS_HAVE_EVP_aes_256_cfb8
+#define LWS_HAVE_EVP_aes_256_cfb128
+#define LWS_HAVE_EVP_aes_128_xts
+#define LWS_HAVE_EXECVPE
+/* #undef LWS_HAVE_LIBCAP */
+#define LWS_HAVE_HMAC_CTX_new
+#define LWS_HAVE_MALLOC_H
+#define LWS_HAVE_MALLOC_TRIM
+#define LWS_HAVE_MALLOC_USABLE_SIZE
+/* #undef LWS_HAVE_mbedtls_net_init */
+/* #undef LWS_HAVE_mbedtls_ssl_conf_alpn_protocols */
+/* #undef LWS_HAVE_mbedtls_ssl_get_alpn_protocol */
+/* #undef LWS_HAVE_mbedtls_ssl_conf_sni */
+/* #undef LWS_HAVE_mbedtls_ssl_set_hs_ca_chain */
+/* #undef LWS_HAVE_mbedtls_ssl_set_hs_own_cert */
+/* #undef LWS_HAVE_mbedtls_ssl_set_hs_authmode */
+/* #undef LWS_HAVE_MBEDTLS_NET_SOCKETS */
+/* #undef LWS_HAVE_NEW_UV_VERSION_H */
+#define LWS_HAVE_OPENSSL_ECDH_H
+#define LWS_HAVE_PIPE2
+#define LWS_HAVE_EVENTFD
+#define LWS_HAVE_PTHREAD_H
+#define LWS_HAVE_RSA_SET0_KEY
+/* #undef LWS_HAVE_RSA_verify_pss_mgf1 */
+#define LWS_HAVE_SSL_CTX_get0_certificate
+#define LWS_HAVE_SSL_CTX_set1_param
+#define LWS_HAVE_SSL_CTX_set_ciphersuites
+#define LWS_HAVE_SSL_EXTRA_CHAIN_CERTS
+#define LWS_HAVE_SSL_get0_alpn_selected
+#define LWS_HAVE_SSL_CTX_EVP_PKEY_new_raw_private_key
+#define LWS_HAVE_SSL_set_alpn_protos
+#define LWS_HAVE_SSL_SET_INFO_CALLBACK
+/* #undef LWS_HAVE__STAT32I64 */
+#define LWS_HAVE_STDINT_H
+/* #undef LWS_HAVE_SYS_CAPABILITY_H */
+#define LWS_HAVE_TLS_CLIENT_METHOD
+#define LWS_HAVE_TLSV1_2_CLIENT_METHOD
+/* #undef LWS_HAVE_UV_VERSION_H */
+#define LWS_HAVE_VFORK
+#define LWS_HAVE_X509_get_key_usage
+#define LWS_HAVE_X509_VERIFY_PARAM_set1_host
+#define LWS_LIBRARY_VERSION "4.0.22"
+#define LWS_LOGGING_BITFIELD_CLEAR 0
+#define LWS_LOGGING_BITFIELD_SET 0
+/* #undef LWS_MINGW_SUPPORT */
+/* #undef LWS_NO_CLIENT */
+#define LWS_NO_DAEMONIZE
+#define LWS_OPENSSL_CLIENT_CERTS "../share"
+#define LWS_OPENSSL_SUPPORT
+/* #undef LWS_PLAT_OPTEE */
+#define LWS_PLAT_UNIX
+/* #undef LWS_PLAT_FREERTOS */
+/* #undef LWS_ROLE_CGI */
+/* #undef LWS_ROLE_DBUS */
+#define LWS_ROLE_H1
+#define LWS_ROLE_H2
+#define LWS_ROLE_RAW
+#define LWS_ROLE_RAW_FILE
+/* #undef LWS_ROLE_RAW_PROXY */
+#define LWS_ROLE_WS
+/* #undef LWS_ROLE_MQTT */
+/* #undef LWS_SHA1_USE_OPENSSL_NAME */
+#define LWS_SSL_CLIENT_USE_OS_CA_CERTS
+/* #undef LWS_SSL_SERVER_WITH_ECDH_CERT */
+/* #undef LWS_WITH_ABSTRACT */
+/* #undef LWS_WITH_ACCESS_LOG */
+/* #undef LWS_WITH_ACME */
+/* #undef LWS_WITH_ALSA */
+/* #undef LWS_WITH_SYS_ASYNC_DNS */
+/* #undef LWS_WITH_BORINGSSL */
+/* #undef LWS_WITH_CGI */
+#define LWS_WITH_CUSTOM_HEADERS
+/* #undef LWS_WITH_DEPRECATED_LWS_DLL */
+/* #undef LWS_WITH_DETAILED_LATENCY */
+#define LWS_WITH_DIR
+/* #undef LWS_WITH_ESP32 */
+/* #undef LWS_HAVE_EVBACKEND_LINUXAIO */
+/* #undef LWS_HAVE_EVBACKEND_IOURING */
+#define LWS_WITH_EXTERNAL_POLL
+#define LWS_WITH_FILE_OPS
+/* #undef LWS_WITH_FSMOUNT */
+/* #undef LWS_WITH_FTS */
+/* #undef LWS_WITH_GENCRYPTO */
+/* #undef LWS_WITH_GENERIC_SESSIONS */
+/* #undef LWS_WITH_GLIB */
+/* #undef LWS_WITH_GTK */
+#define LWS_WITH_HTTP2
+#define LWS_WITH_HTTP_BASIC_AUTH
+/* #undef LWS_WITH_HTTP_BROTLI */
+/* #undef LWS_WITH_HTTP_PROXY */
+/* #undef LWS_WITH_HTTP_STREAM_COMPRESSION */
+#define LWS_WITH_HTTP_UNCOMMON_HEADERS
+#define LWS_WITH_IPV6
+/* #undef LWS_WITH_JOSE */
+#define LWS_WITH_LEJP
+/* #undef LWS_WITH_LIBEV */
+/* #undef LWS_WITH_LIBEVENT */
+/* #undef LWS_WITH_LIBUV */
+#define LWS_WITH_LWSAC
+#define LWS_LOGS_TIMESTAMP
+/* #undef LWS_WITH_MBEDTLS */
+/* #undef LWS_WITH_MINIZ */
+#define LWS_WITH_NETWORK
+/* #undef LWS_WITH_NO_LOGS */
+#define LWS_WITH_CLIENT
+#define LWS_WITHOUT_EXTENSIONS
+#define LWS_WITH_SERVER
+/* #undef LWS_WITH_SPAWN */
+/* #undef LWS_WITH_PEER_LIMITS */
+/* #undef LWS_WITH_PLUGINS */
+/* #undef LWS_WITH_POLARSSL */
+#define LWS_WITH_POLL
+/* #undef LWS_WITH_RANGES */
+/* #undef LWS_WITH_SECURE_STREAMS */
+/* #undef LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM */
+/* #undef LWS_WITH_SECURE_STREAMS_PROXY_API */
+/* #undef LWS_WITH_SELFTESTS */
+#define LWS_WITH_SEQUENCER
+/* #undef LWS_WITH_SERVER_STATUS */
+/* #undef LWS_WITH_SMTP */
+/* #undef LWS_WITH_SOCKS5 */
+/* #undef LWS_WITH_STATEFUL_URLDECODE */
+/* #undef LWS_WITH_STATS */
+/* #undef LWS_WITH_STRUCT_SQLITE3 */
+/* #undef LWS_WITH_STRUCT_JSON */
+/* #undef LWS_WITH_SQLITE3 */
+/* #undef LWS_WITH_SYS_NTPCLIENT */
+/* #undef LWS_WITH_SYS_DHCP_CLIENT */
+/* #undef LWS_WITH_THREADPOOL */
+#define LWS_WITH_TLS
+#define LWS_WITH_UDP
+/* #undef LWS_WITH_UNIX_SOCK */
+/* #undef LWS_WITH_ZIP_FOPS */
+/* #undef USE_OLD_CYASSL */
+/* #undef USE_WOLFSSL */
+
+
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/** @file */
+
+#ifndef LIBWEBSOCKET_H_3060898B846849FF9F88F5DB59B5950C
+#define LIBWEBSOCKET_H_3060898B846849FF9F88F5DB59B5950C
+
+#ifdef __cplusplus
+#include <cstddef>
+#include <cstdarg>
+
+extern "C" {
+#else
+#include <stdarg.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "lws_config.h"
+
+/* place for one-shot opaque forward references */
+
+struct lws_sequencer;
+struct lws_dsh;
+
+/*
+ * CARE: everything using cmake defines needs to be below here
+ */
+
+#define LWS_US_PER_SEC ((lws_usec_t)1000000)
+#define LWS_MS_PER_SEC ((lws_usec_t)1000)
+#define LWS_US_PER_MS ((lws_usec_t)1000)
+#define LWS_NS_PER_US ((lws_usec_t)1000)
+
+#define LWS_KI (1024)
+#define LWS_MI (LWS_KI * 1024)
+#define LWS_GI (LWS_MI * 1024)
+#define LWS_TI ((uint64_t)LWS_GI * 1024)
+#define LWS_PI ((uint64_t)LWS_TI * 1024)
+
+#define LWS_US_TO_MS(x) ((x + (LWS_US_PER_MS / 2)) / LWS_US_PER_MS)
+
+#if defined(LWS_HAS_INTPTR_T)
+#include <stdint.h>
+#define lws_intptr_t intptr_t
+#else
+typedef unsigned long long lws_intptr_t;
+#endif
+
+#if defined(WIN32) || defined(_WIN32)
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <stddef.h>
+#include <basetsd.h>
+#include <io.h>
+#ifndef _WIN32_WCE
+#include <fcntl.h>
+#else
+#define _O_RDONLY 0x0000
+#define O_RDONLY _O_RDONLY
+#endif
+
+#define LWS_INLINE __inline
+#define LWS_VISIBLE
+#define LWS_WARN_UNUSED_RESULT
+#define LWS_WARN_DEPRECATED
+#define LWS_FORMAT(string_index)
+
+#if !defined(LWS_EXTERN)
+#ifdef LWS_DLL
+#ifdef LWS_INTERNAL
+#define LWS_EXTERN extern __declspec(dllexport)
+#else
+#define LWS_EXTERN extern __declspec(dllimport)
+#endif
+#endif
+#endif
+
+#define LWS_INVALID_FILE INVALID_HANDLE_VALUE
+#define LWS_SOCK_INVALID (INVALID_SOCKET)
+#define LWS_O_RDONLY _O_RDONLY
+#define LWS_O_WRONLY _O_WRONLY
+#define LWS_O_CREAT _O_CREAT
+#define LWS_O_TRUNC _O_TRUNC
+
+#ifndef __func__
+#define __func__ __FUNCTION__
+#endif
+
+#else /* NOT WIN32 */
+#include <unistd.h>
+#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
+#include <sys/capability.h>
+#endif
+
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__QNX__) || defined(__OpenBSD__)
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif
+
+#define LWS_INLINE inline
+#define LWS_O_RDONLY O_RDONLY
+#define LWS_O_WRONLY O_WRONLY
+#define LWS_O_CREAT O_CREAT
+#define LWS_O_TRUNC O_TRUNC
+
+#if !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_TA) && !defined(LWS_PLAT_FREERTOS)
+#include <poll.h>
+#include <netdb.h>
+#define LWS_INVALID_FILE -1
+#define LWS_SOCK_INVALID (-1)
+#else
+#define getdtablesize() (30)
+#if defined(LWS_PLAT_FREERTOS)
+#define LWS_INVALID_FILE NULL
+#define LWS_SOCK_INVALID (-1)
+#else
+#define LWS_INVALID_FILE NULL
+#define LWS_SOCK_INVALID (-1)
+#endif
+#endif
+
+#if defined(__GNUC__)
+
+/* warn_unused_result attribute only supported by GCC 3.4 or later */
+#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#define LWS_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+#define LWS_WARN_UNUSED_RESULT
+#endif
+
+#define LWS_VISIBLE __attribute__((visibility("default")))
+#define LWS_WARN_DEPRECATED __attribute__ ((deprecated))
+#define LWS_FORMAT(string_index) __attribute__ ((format(printf, string_index, string_index+1)))
+#else
+#define LWS_VISIBLE
+#define LWS_WARN_UNUSED_RESULT
+#define LWS_WARN_DEPRECATED
+#define LWS_FORMAT(string_index)
+#endif
+
+#if defined(__ANDROID__)
+#include <netinet/in.h>
+#include <unistd.h>
+#endif
+
+#endif
+
+#if defined(LWS_WITH_LIBEV)
+#include <ev.h>
+#endif /* LWS_WITH_LIBEV */
+#ifdef LWS_WITH_LIBUV
+#include <uv.h>
+#ifdef LWS_HAVE_UV_VERSION_H
+#include <uv-version.h>
+#endif
+#ifdef LWS_HAVE_NEW_UV_VERSION_H
+#include <uv/version.h>
+#endif
+#endif /* LWS_WITH_LIBUV */
+#if defined(LWS_WITH_LIBEVENT)
+#include <event2/event.h>
+#endif /* LWS_WITH_LIBEVENT */
+
+#ifndef LWS_EXTERN
+#define LWS_EXTERN extern
+#endif
+
+#ifdef _WIN32
+#define random rand
+#else
+#if !defined(LWS_PLAT_OPTEE)
+#include <sys/time.h>
+#include <unistd.h>
+#endif
+#endif
+
+#if defined(LWS_WITH_TLS)
+
+#ifdef USE_WOLFSSL
+#ifdef USE_OLD_CYASSL
+#ifdef _WIN32
+/*
+ * Include user-controlled settings for windows from
+ * <wolfssl-root>/IDE/WIN/user_settings.h
+ */
+#include <IDE/WIN/user_settings.h>
+#include <cyassl/ctaocrypt/settings.h>
+#else
+#include <cyassl/options.h>
+#endif
+#include <cyassl/openssl/ssl.h>
+#include <cyassl/error-ssl.h>
+
+#else
+#ifdef _WIN32
+/*
+ * Include user-controlled settings for windows from
+ * <wolfssl-root>/IDE/WIN/user_settings.h
+ */
+#include <IDE/WIN/user_settings.h>
+#include <wolfssl/wolfcrypt/settings.h>
+#else
+#include <wolfssl/options.h>
+#endif
+#include <wolfssl/openssl/ssl.h>
+#include <wolfssl/error-ssl.h>
+#endif /* not USE_OLD_CYASSL */
+#else
+#if defined(LWS_WITH_MBEDTLS)
+#if defined(LWS_PLAT_FREERTOS)
+/* this filepath is passed to us but without quotes or <> */
+#if !defined(LWS_AMAZON_RTOS)
+/* AMAZON RTOS has its own setting via MTK_MBEDTLS_CONFIG_FILE */
+#undef MBEDTLS_CONFIG_FILE
+#define MBEDTLS_CONFIG_FILE <mbedtls/esp_config.h>
+#endif
+#endif
+#include <mbedtls/ssl.h>
+#include <mbedtls/entropy.h>
+#include <mbedtls/ctr_drbg.h>
+#else
+#include <openssl/ssl.h>
+#if !defined(LWS_WITH_MBEDTLS)
+#include <openssl/err.h>
+#endif
+#endif
+#endif /* not USE_WOLFSSL */
+#endif
+
+/*
+ * Helpers for pthread mutex in user code... if lws is built for
+ * multiple service threads, these resolve to pthread mutex
+ * operations. In the case LWS_MAX_SMP is 1 (the default), they
+ * are all NOPs and no pthread type or api is referenced.
+ */
+
+#if LWS_MAX_SMP > 1
+
+#include <pthread.h>
+
+#define lws_pthread_mutex(name) pthread_mutex_t name;
+
+static LWS_INLINE void
+lws_pthread_mutex_init(pthread_mutex_t *lock)
+{
+ pthread_mutex_init(lock, NULL);
+}
+
+static LWS_INLINE void
+lws_pthread_mutex_destroy(pthread_mutex_t *lock)
+{
+ pthread_mutex_destroy(lock);
+}
+
+static LWS_INLINE void
+lws_pthread_mutex_lock(pthread_mutex_t *lock)
+{
+ pthread_mutex_lock(lock);
+}
+
+static LWS_INLINE void
+lws_pthread_mutex_unlock(pthread_mutex_t *lock)
+{
+ pthread_mutex_unlock(lock);
+}
+
+#else
+#define lws_pthread_mutex(name)
+#define lws_pthread_mutex_init(_a)
+#define lws_pthread_mutex_destroy(_a)
+#define lws_pthread_mutex_lock(_a)
+#define lws_pthread_mutex_unlock(_a)
+#endif
+
+
+#define CONTEXT_PORT_NO_LISTEN -1
+#define CONTEXT_PORT_NO_LISTEN_SERVER -2
+
+#include <libwebsockets/lws-logs.h>
+
+
+#include <stddef.h>
+
+#ifndef lws_container_of
+#define lws_container_of(P,T,M) ((T *)((char *)(P) - offsetof(T, M)))
+#endif
+#define LWS_ALIGN_TO(x, bou) x += ((bou) - ((x) % (bou))) % (bou)
+
+struct lws;
+
+/* api change list for user code to test against */
+
+#define LWS_FEATURE_SERVE_HTTP_FILE_HAS_OTHER_HEADERS_ARG
+
+/* the struct lws_protocols has the id field present */
+#define LWS_FEATURE_PROTOCOLS_HAS_ID_FIELD
+
+/* you can call lws_get_peer_write_allowance */
+#define LWS_FEATURE_PROTOCOLS_HAS_PEER_WRITE_ALLOWANCE
+
+/* extra parameter introduced in 917f43ab821 */
+#define LWS_FEATURE_SERVE_HTTP_FILE_HAS_OTHER_HEADERS_LEN
+
+/* File operations stuff exists */
+#define LWS_FEATURE_FOPS
+
+
+#if defined(_WIN32)
+#if !defined(LWS_WIN32_HANDLE_TYPES)
+typedef SOCKET lws_sockfd_type;
+typedef HANDLE lws_filefd_type;
+#endif
+
+struct lws_pollfd {
+ lws_sockfd_type fd; /**< file descriptor */
+ SHORT events; /**< which events to respond to */
+ SHORT revents; /**< which events happened */
+};
+#define LWS_POLLHUP (FD_CLOSE)
+#define LWS_POLLIN (FD_READ | FD_ACCEPT)
+#define LWS_POLLOUT (FD_WRITE)
+
+#if !defined(pid_t)
+#define pid_t int
+#endif
+
+#else
+
+
+#if defined(LWS_PLAT_FREERTOS)
+#include <libwebsockets/lws-freertos.h>
+#if defined(LWS_WITH_ESP32)
+#include <libwebsockets/lws-esp32.h>
+#endif
+#else
+typedef int lws_sockfd_type;
+typedef int lws_filefd_type;
+#endif
+
+#if defined(LWS_PLAT_OPTEE)
+#include <time.h>
+struct timeval {
+ time_t tv_sec;
+ unsigned int tv_usec;
+};
+#if defined(LWS_WITH_NETWORK)
+// #include <poll.h>
+#define lws_pollfd pollfd
+
+struct timezone;
+
+int gettimeofday(struct timeval *tv, struct timezone *tz);
+
+ /* Internet address. */
+ struct in_addr {
+ uint32_t s_addr; /* address in network byte order */
+ };
+
+typedef unsigned short sa_family_t;
+typedef unsigned short in_port_t;
+typedef uint32_t socklen_t;
+
+#include <libwebsockets/lws-optee.h>
+
+#if !defined(TEE_SE_READER_NAME_MAX)
+ struct addrinfo {
+ int ai_flags;
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+ socklen_t ai_addrlen;
+ struct sockaddr *ai_addr;
+ char *ai_canonname;
+ struct addrinfo *ai_next;
+ };
+#endif
+
+ssize_t recv(int sockfd, void *buf, size_t len, int flags);
+ssize_t send(int sockfd, const void *buf, size_t len, int flags);
+ssize_t read(int fd, void *buf, size_t count);
+int getsockopt(int sockfd, int level, int optname,
+ void *optval, socklen_t *optlen);
+ int setsockopt(int sockfd, int level, int optname,
+ const void *optval, socklen_t optlen);
+int connect(int sockfd, const struct sockaddr *addr,
+ socklen_t addrlen);
+
+extern int errno;
+
+uint16_t ntohs(uint16_t netshort);
+uint16_t htons(uint16_t hostshort);
+
+int bind(int sockfd, const struct sockaddr *addr,
+ socklen_t addrlen);
+
+
+#define MSG_NOSIGNAL 0x4000
+#define EAGAIN 11
+#define EINTR 4
+#define EWOULDBLOCK EAGAIN
+#define EADDRINUSE 98
+#define INADDR_ANY 0
+#define AF_INET 2
+#define SHUT_WR 1
+#define AF_UNSPEC 0
+#define PF_UNSPEC 0
+#define SOCK_STREAM 1
+#define SOCK_DGRAM 2
+# define AI_PASSIVE 0x0001
+#define IPPROTO_UDP 17
+#define SOL_SOCKET 1
+#define SO_SNDBUF 7
+#define EISCONN 106
+#define EALREADY 114
+#define EINPROGRESS 115
+int shutdown(int sockfd, int how);
+int close(int fd);
+int atoi(const char *nptr);
+long long atoll(const char *nptr);
+
+int socket(int domain, int type, int protocol);
+ int getaddrinfo(const char *node, const char *service,
+ const struct addrinfo *hints,
+ struct addrinfo **res);
+
+ void freeaddrinfo(struct addrinfo *res);
+
+#if !defined(TEE_SE_READER_NAME_MAX)
+struct lws_pollfd
+{
+ int fd; /* File descriptor to poll. */
+ short int events; /* Types of events poller cares about. */
+ short int revents; /* Types of events that actually occurred. */
+};
+#endif
+
+int poll(struct pollfd *fds, int nfds, int timeout);
+
+#define LWS_POLLHUP (0x18)
+#define LWS_POLLIN (1)
+#define LWS_POLLOUT (4)
+#else
+struct lws_pollfd;
+struct sockaddr_in;
+#endif
+#else
+#define lws_pollfd pollfd
+#define LWS_POLLHUP (POLLHUP | POLLERR)
+#define LWS_POLLIN (POLLIN)
+#define LWS_POLLOUT (POLLOUT)
+#endif
+#endif
+
+
+#if (defined(WIN32) || defined(_WIN32)) && !defined(__MINGW32__)
+/* ... */
+#define ssize_t SSIZE_T
+#endif
+
+#if defined(WIN32) && defined(LWS_HAVE__STAT32I64)
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif
+
+#if defined(LWS_HAVE_STDINT_H)
+#include <stdint.h>
+#else
+#if defined(WIN32) || defined(_WIN32)
+/* !!! >:-[ */
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int8 uint8_t;
+#else
+typedef unsigned int uint32_t;
+typedef unsigned short uint16_t;
+typedef unsigned char uint8_t;
+#endif
+#endif
+
+typedef int64_t lws_usec_t;
+typedef unsigned long long lws_filepos_t;
+typedef long long lws_fileofs_t;
+typedef uint32_t lws_fop_flags_t;
+
+#define lws_concat_temp(_t, _l) (_t + sizeof(_t) - _l)
+#define lws_concat_used(_t, _l) (sizeof(_t) - _l)
+
+/** struct lws_pollargs - argument structure for all external poll related calls
+ * passed in via 'in' */
+struct lws_pollargs {
+ lws_sockfd_type fd; /**< applicable socket descriptor */
+ int events; /**< the new event mask */
+ int prev_events; /**< the previous event mask */
+};
+
+struct lws_extension; /* needed even with ws exts disabled for create context */
+struct lws_token_limits;
+struct lws_protocols;
+struct lws_context;
+struct lws_tokens;
+struct lws_vhost;
+struct lws;
+
+#include <libwebsockets/lws-dll2.h>
+#include <libwebsockets/lws-timeout-timer.h>
+#include <libwebsockets/lws-state.h>
+#include <libwebsockets/lws-retry.h>
+#include <libwebsockets/lws-adopt.h>
+#include <libwebsockets/lws-network-helper.h>
+#include <libwebsockets/lws-system.h>
+#include <libwebsockets/lws-detailed-latency.h>
+#include <libwebsockets/lws-ws-close.h>
+#include <libwebsockets/lws-callbacks.h>
+#include <libwebsockets/lws-ws-state.h>
+#include <libwebsockets/lws-ws-ext.h>
+#include <libwebsockets/lws-protocols-plugins.h>
+#include <libwebsockets/lws-plugin-generic-sessions.h>
+#include <libwebsockets/lws-context-vhost.h>
+#if defined(LWS_ROLE_MQTT)
+#include <libwebsockets/lws-mqtt.h>
+#endif
+#include <libwebsockets/lws-client.h>
+#include <libwebsockets/lws-http.h>
+#include <libwebsockets/lws-spa.h>
+#include <libwebsockets/lws-purify.h>
+#include <libwebsockets/lws-misc.h>
+#include <libwebsockets/lws-dsh.h>
+#include <libwebsockets/lws-service.h>
+#include <libwebsockets/lws-write.h>
+#include <libwebsockets/lws-writeable.h>
+#include <libwebsockets/lws-ring.h>
+#include <libwebsockets/lws-sha1-base64.h>
+#include <libwebsockets/lws-x509.h>
+#include <libwebsockets/lws-cgi.h>
+#if defined(LWS_WITH_FILE_OPS)
+#include <libwebsockets/lws-vfs.h>
+#endif
+#include <libwebsockets/lws-lejp.h>
+#include <libwebsockets/lws-stats.h>
+#include <libwebsockets/lws-struct.h>
+#include <libwebsockets/lws-threadpool.h>
+#include <libwebsockets/lws-tokenize.h>
+#include <libwebsockets/lws-lwsac.h>
+#include <libwebsockets/lws-fts.h>
+#include <libwebsockets/lws-diskcache.h>
+#include <libwebsockets/lws-sequencer.h>
+#include <libwebsockets/lws-secure-streams.h>
+#include <libwebsockets/lws-secure-streams-policy.h>
+#include <libwebsockets/lws-secure-streams-client.h>
+
+#if !defined(LWS_PLAT_FREERTOS)
+#include <libwebsockets/abstract/abstract.h>
+
+#include <libwebsockets/lws-test-sequencer.h>
+#endif
+#include <libwebsockets/lws-async-dns.h>
+
+#if defined(LWS_WITH_TLS)
+
+#if defined(LWS_WITH_MBEDTLS)
+#include <mbedtls/md5.h>
+#include <mbedtls/sha1.h>
+#include <mbedtls/sha256.h>
+#include <mbedtls/sha512.h>
+#endif
+
+#include <libwebsockets/lws-gencrypto.h>
+#include <libwebsockets/lws-genhash.h>
+#include <libwebsockets/lws-genrsa.h>
+#include <libwebsockets/lws-genaes.h>
+#include <libwebsockets/lws-genec.h>
+
+#include <libwebsockets/lws-jwk.h>
+#include <libwebsockets/lws-jose.h>
+#include <libwebsockets/lws-jws.h>
+#include <libwebsockets/lws-jwe.h>
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*
+ * These are used to optionally pass an array of index = C string, binary array,
+ * or ulong tokens to the abstract transport or protocol. For example if it's
+ * raw socket transport, then the DNS address to connect to and the port are
+ * passed using these when the client created and bound to the transport.
+ */
+
+typedef struct lws_token_map {
+ union {
+ const char *value;
+ uint8_t *bvalue;
+ unsigned long lvalue;
+ } u;
+ short name_index; /* 0 here indicates end of array */
+ short length_or_zero;
+} lws_token_map_t;
+
+/*
+ * The indvidual protocols and transports define their own name_index-es which
+ * are meaningful to them. Define index 0 globally as the end of an array of
+ * them, and provide bases so user protocol and transport ones don't overlap.
+ */
+
+enum {
+ LTMI_END_OF_ARRAY,
+
+ LTMI_PROTOCOL_BASE = 2048,
+
+ LTMI_TRANSPORT_BASE = 4096
+};
+
+struct lws_abs_transport;
+struct lws_abs_protocol;
+typedef struct lws_abs lws_abs_t;
+
+LWS_VISIBLE LWS_EXTERN const lws_token_map_t *
+lws_abs_get_token(const lws_token_map_t *token_map, short name_index);
+
+/*
+ * the combination of a protocol, transport, and token maps for each
+ */
+
+typedef void lws_abs_transport_inst_t;
+typedef void lws_abs_protocol_inst_t;
+
+/**
+ * lws_abstract_alloc() - allocate and configure an lws_abs_t
+ *
+ * \param vhost: the struct lws_vhost to bind to
+ * \param user: opaque user pointer
+ * \param abstract_path: "protocol.transport" names
+ * \param ap_tokens: tokens for protocol options
+ * \param at_tokens: tokens for transport
+ * \param seq: optional sequencer we should bind to, or NULL
+ * \param opaque_user_data: data given in sequencer callback, if any
+ *
+ * Returns an allocated lws_abs_t pointer set up with the other arguments.
+ *
+ * Doesn't create a connection instance, just allocates the lws_abs_t and
+ * sets it up with the arguments.
+ *
+ * Returns NULL is there's any problem.
+ */
+LWS_VISIBLE LWS_EXTERN lws_abs_t *
+lws_abstract_alloc(struct lws_vhost *vhost, void *user,
+ const char *abstract_path, const lws_token_map_t *ap_tokens,
+ const lws_token_map_t *at_tokens, struct lws_sequencer *seq,
+ void *opaque_user_data);
+
+/**
+ * lws_abstract_free() - free an allocated lws_abs_t
+ *
+ * \param pabs: pointer to the lws_abs_t * to free
+ *
+ * Frees and sets the pointer to NULL.
+ */
+
+LWS_VISIBLE LWS_EXTERN void
+lws_abstract_free(lws_abs_t **pabs);
+
+/**
+ * lws_abs_bind_and_create_instance - use an abstract protocol and transport
+ *
+ * \param abs: the lws_abs_t describing the combination desired
+ *
+ * This instantiates an abstract protocol and abstract transport bound together.
+ * A single heap allocation is made for the combination and the protocol and
+ * transport creation ops are called on it. The ap_tokens and at_tokens
+ * are consulted by the creation ops to decide the details of the protocol and
+ * transport for the instance.
+ */
+LWS_VISIBLE LWS_EXTERN lws_abs_t *
+lws_abs_bind_and_create_instance(const lws_abs_t *ai);
+
+/**
+ * lws_abs_destroy_instance() - destroys an instance
+ *
+ * \param ai: pointer to the ai pointer to destroy
+ *
+ * This is for destroying an instance created by
+ * lws_abs_bind_and_create_instance() above.
+ *
+ * Calls the protocol and transport destroy operations on the instance, then
+ * frees the combined allocation in one step. The pointer ai is set to NULL.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_abs_destroy_instance(lws_abs_t **ai);
+
+/*
+ * bring in all the protocols and transports definitions
+ */
+
+#include <libwebsockets/abstract/protocols.h>
+#include <libwebsockets/abstract/transports.h>
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*
+ * Information about how this protocol handles multiple use of connections.
+ *
+ * .flags of 0 indicates each connection must start with a fresh transport.
+ *
+ * Flags can be used to indicate the protocol itself supports different
+ * kinds of multiple use. However the actual use or not of these may depend on
+ * negotiation with the remote peer.
+ *
+ * LWS_AP_FLAG_PIPELINE_TRANSACTIONS: other instances can be queued on one
+ * with an existing connection and get a
+ * chance to "hot take over" the existing
+ * transport in turn, like h1 keepalive
+ * pipelining
+ *
+ * LWS_AP_FLAG_MUXABLE_STREAM: an existing connection can absorb more child
+ * connections and mux them as separate child
+ * streams ongoing, like h2
+ */
+
+enum {
+ LWS_AP_FLAG_PIPELINE_TRANSACTIONS = (1 << 0),
+ LWS_AP_FLAG_MUXABLE_STREAM = (1 << 1),
+};
+
+typedef struct lws_abs_protocol {
+ const char *name;
+ int alloc;
+ int flags;
+
+ int (*create)(const struct lws_abs *ai);
+ void (*destroy)(lws_abs_protocol_inst_t **d);
+ int (*compare)(lws_abs_t *abs1, lws_abs_t *abs2);
+
+ /* events the transport invokes (handled by abstract protocol) */
+
+ int (*accept)(lws_abs_protocol_inst_t *d);
+ int (*rx)(lws_abs_protocol_inst_t *d, const uint8_t *b, size_t l);
+ int (*writeable)(lws_abs_protocol_inst_t *d, size_t budget);
+ int (*closed)(lws_abs_protocol_inst_t *d);
+ int (*heartbeat)(lws_abs_protocol_inst_t *d);
+
+ /* as parent, we get a notification a new child / queue entry
+ * bound to us... this is the parent lws_abs_t as arg */
+ int (*child_bind)(lws_abs_t *abs);
+} lws_abs_protocol_t;
+
+/**
+ * lws_abs_protocol_get_by_name() - returns a pointer to the named protocol ops
+ *
+ * \param name: the name of the abstract protocol
+ *
+ * Returns a pointer to the named protocol ops struct if available, otherwise
+ * NULL.
+ */
+LWS_VISIBLE LWS_EXTERN const lws_abs_protocol_t *
+lws_abs_protocol_get_by_name(const char *name);
+
+/*
+ * bring in public api pieces from protocols
+ */
+
+#include <libwebsockets/abstract/protocols/smtp.h>
+
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/** \defgroup smtp SMTP related functions
+ * ##SMTP related functions
+ * \ingroup lwsapi
+ *
+ * These apis let you communicate with a local SMTP server to send email from
+ * lws. It handles all the SMTP sequencing and protocol actions.
+ *
+ * Your system should have postfix, sendmail or another MTA listening on port
+ * 25 and able to send email using the "mail" commandline app. Usually distro
+ * MTAs are configured for this by default.
+ *
+ * You can either use the abstract protocol layer directly, or instead use the
+ * provided smtp sequencer... this takes care of creating the protocol
+ * connections, and provides and email queue and retry management.
+ */
+//@{
+
+#if defined(LWS_WITH_SMTP)
+
+enum {
+ LTMI_PSMTP_V_HELO = LTMI_PROTOCOL_BASE, /* u.value */
+
+ LTMI_PSMTP_V_LWS_SMTP_EMAIL_T, /* u.value */
+};
+
+enum {
+ LWS_SMTP_DISPOSITION_SENT,
+ LWS_SMTP_DISPOSITION_FAILED,
+ LWS_SMTP_DISPOSITION_FAILED_DESTROY
+};
+
+typedef struct lws_smtp_sequencer_args {
+ const char helo[32];
+ struct lws_vhost *vhost;
+ time_t retry_interval;
+ time_t delivery_timeout;
+ size_t email_queue_max;
+ size_t max_content_size;
+} lws_smtp_sequencer_args_t;
+
+typedef struct lws_smtp_sequencer lws_smtp_sequencer_t;
+typedef struct lws_smtp_email lws_smtp_email_t;
+
+LWS_VISIBLE LWS_EXTERN lws_smtp_sequencer_t *
+lws_smtp_sequencer_create(const lws_smtp_sequencer_args_t *args);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_smtp_sequencer_destroy(lws_smtp_sequencer_t *s);
+
+typedef int (*lws_smtp_cb_t)(void *e, void *d, int disp, const void *b, size_t l);
+typedef struct lws_smtp_email lws_smtp_email_t;
+
+/**
+ * lws_smtpc_add_email() - Allocates and queues an email object
+ *
+ * \param s: smtp sequencer to queue on
+ * \param payload: the email payload string, with headers and terminating .
+ * \param payload_len: size in bytes of the payload string
+ * \param sender: the sender name and email
+ * \param recipient: the recipient name and email
+ * \param data: opaque user data returned in the done callback
+ * \param done: callback called when the email send succeeded or failed
+ *
+ * Allocates an email object and copies the payload, sender and recipient into
+ * it and initializes it. Returns NULL if OOM, otherwise the allocated email
+ * object.
+ *
+ * Because it copies the arguments into an allocated buffer, the original
+ * arguments can be safely destroyed after calling this.
+ *
+ * The done() callback must free the email object. It doesn't have to free any
+ * individual members.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_smtpc_add_email(lws_smtp_sequencer_t *s, const char *payload,
+ size_t payload_len, const char *sender,
+ const char *recipient, void *data, lws_smtp_cb_t done);
+
+/**
+ * lws_smtpc_free_email() - Add email to the list of ones being sent
+ *
+ * \param e: email to queue for sending on \p c
+ *
+ * Adds an email to the linked-list of emails to send
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_smtpc_free_email(lws_smtp_email_t *e);
+
+
+#endif
+//@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*
+ * Abstract transport ops
+ */
+
+typedef struct lws_abs_transport {
+ const char *name;
+ int alloc;
+
+ int (*create)(lws_abs_t *abs);
+ void (*destroy)(lws_abs_transport_inst_t **d);
+
+ /* check if the transport settings for these connections are the same */
+ int (*compare)(lws_abs_t *abs1, lws_abs_t *abs2);
+
+ /* events the abstract protocol invokes (handled by transport) */
+
+ int (*tx)(lws_abs_transport_inst_t *d, uint8_t *buf, size_t len);
+ int (*client_conn)(const lws_abs_t *abs);
+ int (*close)(lws_abs_transport_inst_t *d);
+ int (*ask_for_writeable)(lws_abs_transport_inst_t *d);
+ int (*set_timeout)(lws_abs_transport_inst_t *d, int reason, int secs);
+ int (*state)(lws_abs_transport_inst_t *d);
+} lws_abs_transport_t;
+
+/**
+ * lws_abs_protocol_get_by_name() - returns a pointer to the named protocol ops
+ *
+ * \param name: the name of the abstract protocol
+ *
+ * Returns a pointer to the named protocol ops struct if available, otherwise
+ * NULL.
+ */
+LWS_VISIBLE LWS_EXTERN const lws_abs_transport_t *
+lws_abs_transport_get_by_name(const char *name);
+
+/*
+ * bring in public api pieces from transports
+ */
+
+#include <libwebsockets/abstract/transports/raw-skt.h>
+#include <libwebsockets/abstract/transports/unit-test.h>
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+enum {
+ LTMI_PEER_V_DNS_ADDRESS = LTMI_TRANSPORT_BASE, /* u.value */
+ LTMI_PEER_LV_PORT, /* u.lvalue */
+ LTMI_PEER_LV_TLS_FLAGS, /* u.lvalue */
+};
--- /dev/null
+ /*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * This is an abstract transport useful for unit testing abstract protocols.
+ *
+ * Instead of passing data anywhere, you give the transport a list of packets
+ * to deliver and packets you expect back from the abstract protocol it's
+ * bound to.
+ */
+
+enum {
+ LWS_AUT_EXPECT_TEST_END = (1 << 0),
+ LWS_AUT_EXPECT_LOCAL_CLOSE = (1 << 1),
+ LWS_AUT_EXPECT_DO_REMOTE_CLOSE = (1 << 2),
+ LWS_AUT_EXPECT_TX /* expect this as tx from protocol */ = (1 << 3),
+ LWS_AUT_EXPECT_RX /* present this as rx to protocol */ = (1 << 4),
+ LWS_AUT_EXPECT_SHOULD_FAIL = (1 << 5),
+ LWS_AUT_EXPECT_SHOULD_TIMEOUT = (1 << 6),
+};
+
+typedef enum {
+ LPE_CONTINUE,
+ LPE_SUCCEEDED,
+ LPE_FAILED,
+ LPE_FAILED_UNEXPECTED_TIMEOUT,
+ LPE_FAILED_UNEXPECTED_PASS,
+ LPE_FAILED_UNEXPECTED_CLOSE,
+ LPE_SKIPPED,
+ LPE_CLOSING
+} lws_unit_test_packet_disposition;
+
+typedef int (*lws_unit_test_packet_test_cb)(const void *cb_user, int disposition);
+typedef int (*lws_unit_test_packet_cb)(lws_abs_t *instance);
+
+/* each step in the unit test */
+
+typedef struct lws_unit_test_packet {
+ void *buffer;
+ lws_unit_test_packet_cb pre;
+ size_t len;
+
+ uint32_t flags;
+} lws_unit_test_packet_t;
+
+/* each unit test */
+
+typedef struct lws_unit_test {
+ const char * name; /* NULL indicates end of test array */
+ lws_unit_test_packet_t * expect_array;
+ int max_secs;
+} lws_unit_test_t;
+
+enum {
+ LTMI_PEER_V_EXPECT_TEST = LTMI_TRANSPORT_BASE, /* u.value */
+ LTMI_PEER_V_EXPECT_RESULT_CB, /* u.value */
+ LTMI_PEER_V_EXPECT_RESULT_CB_ARG, /* u.value */
+};
+
+LWS_VISIBLE LWS_EXTERN const char *
+lws_unit_test_result_name(int in);
+
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/** \defgroup sock-adopt Socket adoption helpers
+ * ##Socket adoption helpers
+ *
+ * When integrating with an external app with its own event loop, these can
+ * be used to accept connections from someone else's listening socket.
+ *
+ * When using lws own event loop, these are not needed.
+ */
+///@{
+
+/**
+ * lws_adopt_socket() - adopt foreign socket as if listen socket accepted it
+ * for the default vhost of context.
+ *
+ * \param context: lws context
+ * \param accept_fd: fd of already-accepted socket to adopt
+ *
+ * Either returns new wsi bound to accept_fd, or closes accept_fd and
+ * returns NULL, having cleaned up any new wsi pieces.
+ *
+ * LWS adopts the socket in http serving mode, it's ready to accept an upgrade
+ * to ws or just serve http.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws *
+lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd);
+/**
+ * lws_adopt_socket_vhost() - adopt foreign socket as if listen socket accepted
+ * it for vhost
+ *
+ * \param vh: lws vhost
+ * \param accept_fd: fd of already-accepted socket to adopt
+ *
+ * Either returns new wsi bound to accept_fd, or closes accept_fd and
+ * returns NULL, having cleaned up any new wsi pieces.
+ *
+ * LWS adopts the socket in http serving mode, it's ready to accept an upgrade
+ * to ws or just serve http.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws *
+lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd);
+
+typedef enum {
+ LWS_ADOPT_RAW_FILE_DESC = 0, /* convenience constant */
+ LWS_ADOPT_HTTP = 1, /* flag: absent implies RAW */
+ LWS_ADOPT_SOCKET = 2, /* flag: absent implies file descr */
+ LWS_ADOPT_ALLOW_SSL = 4, /* flag: if set requires LWS_ADOPT_SOCKET */
+ LWS_ADOPT_FLAG_UDP = 16, /* flag: socket is UDP */
+ LWS_ADOPT_FLAG_RAW_PROXY = 32, /* flag: raw proxy */
+
+ LWS_ADOPT_RAW_SOCKET_UDP = LWS_ADOPT_SOCKET | LWS_ADOPT_FLAG_UDP,
+} lws_adoption_type;
+
+typedef union {
+ lws_sockfd_type sockfd;
+ lws_filefd_type filefd;
+} lws_sock_file_fd_type;
+
+#if defined(LWS_WITH_UDP)
+struct lws_udp {
+ struct sockaddr sa;
+ socklen_t salen;
+
+ struct sockaddr sa_pending;
+ socklen_t salen_pending;
+};
+#endif
+
+/**
+* lws_adopt_descriptor_vhost() - adopt foreign socket or file descriptor
+* if socket descriptor, should already have been accepted from listen socket
+*
+* \param vh: lws vhost
+* \param type: OR-ed combinations of lws_adoption_type flags
+* \param fd: union with either .sockfd or .filefd set
+* \param vh_prot_name: NULL or vh protocol name to bind raw connection to
+* \param parent: NULL or struct lws to attach new_wsi to as a child
+*
+* Either returns new wsi bound to accept_fd, or closes accept_fd and
+* returns NULL, having cleaned up any new wsi pieces.
+*
+* If LWS_ADOPT_SOCKET is set, LWS adopts the socket in http serving mode, it's
+* ready to accept an upgrade to ws or just serve http.
+*
+* parent may be NULL, if given it should be an existing wsi that will become the
+* parent of the new wsi created by this call.
+*/
+LWS_VISIBLE LWS_EXTERN struct lws *
+lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
+ lws_sock_file_fd_type fd, const char *vh_prot_name,
+ struct lws *parent);
+
+typedef struct lws_adopt_desc {
+ struct lws_vhost *vh; /**< vhost the wsi should belong to */
+ lws_adoption_type type; /**< OR-ed combinations of lws_adoption_type flags */
+ lws_sock_file_fd_type fd; /**< union with either .sockfd or .filefd set */
+ const char *vh_prot_name; /**< NULL or vh protocol name to bind raw connection to */
+ struct lws *parent; /**< NULL or struct lws to attach new_wsi to as a child */
+ void *opaque; /**< opaque pointer to set on created wsi */
+} lws_adopt_desc_t;
+
+/**
+* lws_adopt_descriptor_vhost_via_info() - adopt foreign socket or file descriptor
+* if socket descriptor, should already have been accepted from listen socket
+*
+* \param info: the struct containing the parameters
+*
+* - vh: lws vhost
+* - type: OR-ed combinations of lws_adoption_type flags
+* - fd: union with either .sockfd or .filefd set
+* - vh_prot_name: NULL or vh protocol name to bind raw connection to
+* - parent: NULL or struct lws to attach new_wsi to as a child
+* - opaque: opaque pointer to set on created wsi
+*
+* Either returns new wsi bound to accept_fd, or closes accept_fd and
+* returns NULL, having cleaned up any new wsi pieces.
+*
+* If LWS_ADOPT_SOCKET is set, LWS adopts the socket in http serving mode, it's
+* ready to accept an upgrade to ws or just serve http.
+*
+* parent may be NULL, if given it should be an existing wsi that will become the
+* parent of the new wsi created by this call.
+*/
+LWS_VISIBLE LWS_EXTERN struct lws *
+lws_adopt_descriptor_vhost_via_info(const lws_adopt_desc_t *info);
+
+/**
+ * lws_adopt_socket_readbuf() - adopt foreign socket and first rx as if listen socket accepted it
+ * for the default vhost of context.
+ * \param context: lws context
+ * \param accept_fd: fd of already-accepted socket to adopt
+ * \param readbuf: NULL or pointer to data that must be drained before reading from
+ * accept_fd
+ * \param len: The length of the data held at \p readbuf
+ *
+ * Either returns new wsi bound to accept_fd, or closes accept_fd and
+ * returns NULL, having cleaned up any new wsi pieces.
+ *
+ * LWS adopts the socket in http serving mode, it's ready to accept an upgrade
+ * to ws or just serve http.
+ *
+ * If your external code did not already read from the socket, you can use
+ * lws_adopt_socket() instead.
+ *
+ * This api is guaranteed to use the data at \p readbuf first, before reading from
+ * the socket.
+ *
+ * \p readbuf is limited to the size of the ah rx buf, currently 2048 bytes.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws *
+lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd,
+ const char *readbuf, size_t len);
+/**
+ * lws_adopt_socket_vhost_readbuf() - adopt foreign socket and first rx as if listen socket
+ * accepted it for vhost.
+ * \param vhost: lws vhost
+ * \param accept_fd: fd of already-accepted socket to adopt
+ * \param readbuf: NULL or pointer to data that must be drained before reading from accept_fd
+ * \param len: The length of the data held at \p readbuf
+ *
+ * Either returns new wsi bound to accept_fd, or closes accept_fd and
+ * returns NULL, having cleaned up any new wsi pieces.
+ *
+ * LWS adopts the socket in http serving mode, it's ready to accept an upgrade
+ * to ws or just serve http.
+ *
+ * If your external code did not already read from the socket, you can use
+ * lws_adopt_socket() instead.
+ *
+ * This api is guaranteed to use the data at \p readbuf first, before reading from
+ * the socket.
+ *
+ * \p readbuf is limited to the size of the ah rx buf, currently 2048 bytes.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws *
+lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost,
+ lws_sockfd_type accept_fd, const char *readbuf,
+ size_t len);
+
+#define LWS_CAUDP_BIND (1 << 0)
+#define LWS_CAUDP_BROADCAST (1 << 1)
+#define LWS_CAUDP_PF_PACKET (1 << 2)
+
+#if defined(LWS_WITH_UDP)
+/**
+ * lws_create_adopt_udp() - create, bind and adopt a UDP socket
+ *
+ * \param vhost: lws vhost
+ * \param ads: NULL or address to do dns lookup on
+ * \param port: UDP port to bind to, -1 means unbound
+ * \param flags: 0 or LWS_CAUDP_NO_BIND
+ * \param protocol_name: Name of protocol on vhost to bind wsi to
+ * \param ifname: NULL, for network interface name to bind socket to
+ * \param parent_wsi: NULL or parent wsi new wsi will be a child of
+ * \param opaque: set created wsi opaque ptr to this
+ * \param retry_policy: NULL for vhost default policy else wsi specific policy
+ *
+ * Either returns new wsi bound to accept_fd, or closes accept_fd and
+ * returns NULL, having cleaned up any new wsi pieces.
+ * */
+LWS_VISIBLE LWS_EXTERN struct lws *
+lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port,
+ int flags, const char *protocol_name, const char *ifname,
+ struct lws *parent_wsi, void *opaque,
+ const lws_retry_bo_t *retry_policy);
+#endif
+
+
+
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#if defined(LWS_WITH_UDP)
+
+typedef enum dns_query_type {
+ LWS_ADNS_RECORD_A = 0x01,
+ LWS_ADNS_RECORD_CNAME = 0x05,
+ LWS_ADNS_RECORD_MX = 0x0f,
+ LWS_ADNS_RECORD_AAAA = 0x1c,
+} adns_query_type_t;
+
+typedef enum {
+ LADNS_RET_FAILED_WSI_CLOSED = -4,
+ LADNS_RET_NXDOMAIN = -3,
+ LADNS_RET_TIMEDOUT = -2,
+ LADNS_RET_FAILED = -1,
+ LADNS_RET_FOUND,
+ LADNS_RET_CONTINUING
+} lws_async_dns_retcode_t;
+
+typedef struct lws * (*lws_async_dns_cb_t)(struct lws *wsi, const char *ads,
+ const struct addrinfo *result, int n,
+ void *opaque);
+
+/**
+ * lws_async_dns_query() - perform a dns lookup using async dns
+ *
+ * \param context: the lws_context
+ * \param tsi: thread service index (usually 0)
+ * \param name: DNS name to look up
+ * \param qtype: type of query (A, AAAA etc)
+ * \param cb: query completion callback
+ * \param wsi: wsi if the query is related to one
+ *
+ * Starts an asynchronous DNS lookup, on completion the \p cb callback will
+ * be called.
+ *
+ * The reference count on the cached object is incremented for every callback
+ * that was called with the cached addrinfo results.
+ *
+ * The cached object can't be evicted until the reference count reaches zero...
+ * use lws_async_dns_freeaddrinfo() to indicate you're finsihed with the
+ * results for each callback that happened with them.
+ */
+LWS_VISIBLE LWS_EXTERN lws_async_dns_retcode_t
+lws_async_dns_query(struct lws_context *context, int tsi, const char *name,
+ adns_query_type_t qtype, lws_async_dns_cb_t cb,
+ struct lws *wsi, void *opaque);
+
+/**
+ * lws_async_dns_freeaddrinfo() - decrement refcount on cached addrinfo results
+ *
+ * \param pai: a pointert to a pointer to first addrinfo returned as result in the callback
+ *
+ * Decrements the cache object's reference count. When it reaches zero, the
+ * cached object may be reaped subject to LRU rules.
+ *
+ * The pointer to the first addrinfo give in the argument is set to NULL.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_async_dns_freeaddrinfo(const struct addrinfo **ai);
+
+#endif
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*! \defgroup usercb User Callback
+ *
+ * ##User protocol callback
+ *
+ * The protocol callback is the primary way lws interacts with
+ * user code. For one of a list of a few dozen reasons the callback gets
+ * called at some event to be handled.
+ *
+ * All of the events can be ignored, returning 0 is taken as "OK" and returning
+ * nonzero in most cases indicates that the connection should be closed.
+ */
+///@{
+
+struct lws_ssl_info {
+ int where;
+ int ret;
+};
+
+enum lws_cert_update_state {
+ LWS_CUS_IDLE,
+ LWS_CUS_STARTING,
+ LWS_CUS_SUCCESS,
+ LWS_CUS_FAILED,
+
+ LWS_CUS_CREATE_KEYS,
+ LWS_CUS_REG,
+ LWS_CUS_AUTH,
+ LWS_CUS_CHALLENGE,
+ LWS_CUS_CREATE_REQ,
+ LWS_CUS_REQ,
+ LWS_CUS_CONFIRM,
+ LWS_CUS_ISSUE,
+};
+
+enum {
+ LWS_TLS_REQ_ELEMENT_COUNTRY,
+ LWS_TLS_REQ_ELEMENT_STATE,
+ LWS_TLS_REQ_ELEMENT_LOCALITY,
+ LWS_TLS_REQ_ELEMENT_ORGANIZATION,
+ LWS_TLS_REQ_ELEMENT_COMMON_NAME,
+ LWS_TLS_REQ_ELEMENT_SUBJECT_ALT_NAME,
+ LWS_TLS_REQ_ELEMENT_EMAIL,
+
+ LWS_TLS_REQ_ELEMENT_COUNT,
+
+ LWS_TLS_SET_DIR_URL = LWS_TLS_REQ_ELEMENT_COUNT,
+ LWS_TLS_SET_AUTH_PATH,
+ LWS_TLS_SET_CERT_PATH,
+ LWS_TLS_SET_KEY_PATH,
+
+ LWS_TLS_TOTAL_COUNT
+};
+
+struct lws_acme_cert_aging_args {
+ struct lws_vhost *vh;
+ const char *element_overrides[LWS_TLS_TOTAL_COUNT]; /* NULL = use pvo */
+};
+
+/*
+ * NOTE: These public enums are part of the abi. If you want to add one,
+ * add it at where specified so existing users are unaffected.
+ */
+/** enum lws_callback_reasons - reason you're getting a protocol callback */
+enum lws_callback_reasons {
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to wsi and protocol binding lifecycle -----
+ */
+
+ LWS_CALLBACK_PROTOCOL_INIT = 27,
+ /**< One-time call per protocol, per-vhost using it, so it can
+ * do initial setup / allocations etc */
+
+ LWS_CALLBACK_PROTOCOL_DESTROY = 28,
+ /**< One-time call per protocol, per-vhost using it, indicating
+ * this protocol won't get used at all after this callback, the
+ * vhost is getting destroyed. Take the opportunity to
+ * deallocate everything that was allocated by the protocol. */
+
+ LWS_CALLBACK_WSI_CREATE = 29,
+ /**< outermost (earliest) wsi create notification to protocols[0] */
+
+ LWS_CALLBACK_WSI_DESTROY = 30,
+ /**< outermost (latest) wsi destroy notification to protocols[0] */
+
+ LWS_CALLBACK_WSI_TX_CREDIT_GET = 103,
+ /**< manually-managed connection received TX credit (len is int32) */
+
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to Server TLS -----
+ */
+
+ LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS = 21,
+ /**< if configured for
+ * including OpenSSL support, this callback allows your user code
+ * to perform extra SSL_CTX_load_verify_locations() or similar
+ * calls to direct OpenSSL where to find certificates the client
+ * can use to confirm the remote server identity. user is the
+ * OpenSSL SSL_CTX* */
+
+ LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS = 22,
+ /**< if configured for
+ * including OpenSSL support, this callback allows your user code
+ * to load extra certificates into the server which allow it to
+ * verify the validity of certificates returned by clients. user
+ * is the server's OpenSSL SSL_CTX* and in is the lws_vhost */
+
+ LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION = 23,
+ /**< if the libwebsockets vhost was created with the option
+ * LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT, then this
+ * callback is generated during OpenSSL verification of the cert
+ * sent from the client. It is sent to protocol[0] callback as
+ * no protocol has been negotiated on the connection yet.
+ * Notice that the libwebsockets context and wsi are both NULL
+ * during this callback. See
+ * http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html
+ * to understand more detail about the OpenSSL callback that
+ * generates this libwebsockets callback and the meanings of the
+ * arguments passed. In this callback, user is the x509_ctx,
+ * in is the ssl pointer and len is preverify_ok
+ * Notice that this callback maintains libwebsocket return
+ * conventions, return 0 to mean the cert is OK or 1 to fail it.
+ * This also means that if you don't handle this callback then
+ * the default callback action of returning 0 allows the client
+ * certificates. */
+
+ LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY = 37,
+ /**< if configured for including OpenSSL support but no private key
+ * file has been specified (ssl_private_key_filepath is NULL), this is
+ * called to allow the user to set the private key directly via
+ * libopenssl and perform further operations if required; this might be
+ * useful in situations where the private key is not directly accessible
+ * by the OS, for example if it is stored on a smartcard.
+ * user is the server's OpenSSL SSL_CTX* */
+
+ LWS_CALLBACK_SSL_INFO = 67,
+ /**< SSL connections only. An event you registered an
+ * interest in at the vhost has occurred on a connection
+ * using the vhost. in is a pointer to a
+ * struct lws_ssl_info containing information about the
+ * event*/
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to Client TLS -----
+ */
+
+ LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION = 58,
+ /**< Similar to LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION
+ * this callback is called during OpenSSL verification of the cert
+ * sent from the server to the client. It is sent to protocol[0]
+ * callback as no protocol has been negotiated on the connection yet.
+ * Notice that the wsi is set because lws_client_connect_via_info was
+ * successful.
+ *
+ * See http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html
+ * to understand more detail about the OpenSSL callback that
+ * generates this libwebsockets callback and the meanings of the
+ * arguments passed. In this callback, user is the x509_ctx,
+ * in is the ssl pointer and len is preverify_ok.
+ *
+ * THIS IS NOT RECOMMENDED BUT if a cert validation error shall be
+ * overruled and cert shall be accepted as ok,
+ * X509_STORE_CTX_set_error((X509_STORE_CTX*)user, X509_V_OK); must be
+ * called and return value must be 0 to mean the cert is OK;
+ * returning 1 will fail the cert in any case.
+ *
+ * This also means that if you don't handle this callback then
+ * the default callback action of returning 0 will not accept the
+ * certificate in case of a validation error decided by the SSL lib.
+ *
+ * This is expected and secure behaviour when validating certificates.
+ *
+ * Note: LCCSCF_ALLOW_SELFSIGNED and
+ * LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK still work without this
+ * callback being implemented.
+ */
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to HTTP Server -----
+ */
+
+ LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED = 19,
+ /**< A new client has been accepted by the ws server. This
+ * callback allows setting any relevant property to it. Because this
+ * happens immediately after the instantiation of a new client,
+ * there's no websocket protocol selected yet so this callback is
+ * issued only to protocol 0. Only wsi is defined, pointing to the
+ * new client, and the return value is ignored. */
+
+ LWS_CALLBACK_HTTP = 12,
+ /**< an http request has come from a client that is not
+ * asking to upgrade the connection to a websocket
+ * one. This is a chance to serve http content,
+ * for example, to send a script to the client
+ * which will then open the websockets connection.
+ * in points to the URI path requested and
+ * lws_serve_http_file() makes it very
+ * simple to send back a file to the client.
+ * Normally after sending the file you are done
+ * with the http connection, since the rest of the
+ * activity will come by websockets from the script
+ * that was delivered by http, so you will want to
+ * return 1; to close and free up the connection. */
+
+ LWS_CALLBACK_HTTP_BODY = 13,
+ /**< the next len bytes data from the http
+ * request body HTTP connection is now available in in. */
+
+ LWS_CALLBACK_HTTP_BODY_COMPLETION = 14,
+ /**< the expected amount of http request body has been delivered */
+
+ LWS_CALLBACK_HTTP_FILE_COMPLETION = 15,
+ /**< a file requested to be sent down http link has completed. */
+
+ LWS_CALLBACK_HTTP_WRITEABLE = 16,
+ /**< you can write more down the http protocol link now. */
+
+ LWS_CALLBACK_CLOSED_HTTP = 5,
+ /**< when a HTTP (non-websocket) session ends */
+
+ LWS_CALLBACK_FILTER_HTTP_CONNECTION = 18,
+ /**< called when the request has
+ * been received and parsed from the client, but the response is
+ * not sent yet. Return non-zero to disallow the connection.
+ * user is a pointer to the connection user space allocation,
+ * in is the URI, eg, "/"
+ * In your handler you can use the public APIs
+ * lws_hdr_total_length() / lws_hdr_copy() to access all of the
+ * headers using the header enums lws_token_indexes from
+ * libwebsockets.h to check for and read the supported header
+ * presence and content before deciding to allow the http
+ * connection to proceed or to kill the connection. */
+
+ LWS_CALLBACK_ADD_HEADERS = 53,
+ /**< This gives your user code a chance to add headers to a server
+ * transaction bound to your protocol. `in` points to a
+ * `struct lws_process_html_args` describing a buffer and length
+ * you can add headers into using the normal lws apis.
+ *
+ * (see LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER to add headers to
+ * a client transaction)
+ *
+ * Only `args->p` and `args->len` are valid, and `args->p` should
+ * be moved on by the amount of bytes written, if any. Eg
+ *
+ * case LWS_CALLBACK_ADD_HEADERS:
+ *
+ * struct lws_process_html_args *args =
+ * (struct lws_process_html_args *)in;
+ *
+ * if (lws_add_http_header_by_name(wsi,
+ * (unsigned char *)"set-cookie:",
+ * (unsigned char *)cookie, cookie_len,
+ * (unsigned char **)&args->p,
+ * (unsigned char *)args->p + args->max_len))
+ * return 1;
+ *
+ * break;
+ */
+
+ LWS_CALLBACK_VERIFY_BASIC_AUTHORIZATION = 102,
+ /**< This gives the user code a chance to accept or reject credentials
+ * provided HTTP to basic authorization. It will only be called if the
+ * http mount's authentication_mode is set to LWSAUTHM_BASIC_AUTH_CALLBACK
+ * `in` points to a credential string of the form `username:password` If
+ * the callback returns zero (the default if unhandled), then the
+ * transaction ends with HTTP_STATUS_UNAUTHORIZED, otherwise the request
+ * will be processed */
+
+ LWS_CALLBACK_CHECK_ACCESS_RIGHTS = 51,
+ /**< This gives the user code a chance to forbid an http access.
+ * `in` points to a `struct lws_process_html_args`, which
+ * describes the URL, and a bit mask describing the type of
+ * authentication required. If the callback returns nonzero,
+ * the transaction ends with HTTP_STATUS_UNAUTHORIZED. */
+
+ LWS_CALLBACK_PROCESS_HTML = 52,
+ /**< This gives your user code a chance to mangle outgoing
+ * HTML. `in` points to a `struct lws_process_html_args`
+ * which describes the buffer containing outgoing HTML.
+ * The buffer may grow up to `.max_len` (currently +128
+ * bytes per buffer).
+ */
+
+ LWS_CALLBACK_HTTP_BIND_PROTOCOL = 49,
+ /**< By default, all HTTP handling is done in protocols[0].
+ * However you can bind different protocols (by name) to
+ * different parts of the URL space using callback mounts. This
+ * callback occurs in the new protocol when a wsi is bound
+ * to that protocol. Any protocol allocation related to the
+ * http transaction processing should be created then.
+ * These specific callbacks are necessary because with HTTP/1.1,
+ * a single connection may perform at series of different
+ * transactions at different URLs, thus the lifetime of the
+ * protocol bind is just for one transaction, not connection. */
+
+ LWS_CALLBACK_HTTP_DROP_PROTOCOL = 50,
+ /**< This is called when a transaction is unbound from a protocol.
+ * It indicates the connection completed its transaction and may
+ * do something different now. Any protocol allocation related
+ * to the http transaction processing should be destroyed. */
+
+ LWS_CALLBACK_HTTP_CONFIRM_UPGRADE = 86,
+ /**< This is your chance to reject an HTTP upgrade action. The
+ * name of the protocol being upgraded to is in 'in', and the ah
+ * is still bound to the wsi, so you can look at the headers.
+ *
+ * The default of returning 0 (ie, also if not handled) means the
+ * upgrade may proceed. Return <0 to just hang up the connection,
+ * or >0 if you have rejected the connection by returning http headers
+ * and response code yourself.
+ *
+ * There is no need for you to call transaction_completed() as the
+ * caller will take care of it when it sees you returned >0.
+ */
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to HTTP Client -----
+ */
+
+ LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP = 44,
+ /**< The HTTP client connection has succeeded, and is now
+ * connected to the server */
+
+ LWS_CALLBACK_CLOSED_CLIENT_HTTP = 45,
+ /**< The HTTP client connection is closing */
+
+ LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ = 48,
+ /**< This is generated by lws_http_client_read() used to drain
+ * incoming data. In the case the incoming data was chunked, it will
+ * be split into multiple smaller callbacks for each chunk block,
+ * removing the chunk headers. If not chunked, it will appear all in
+ * one callback. */
+
+ LWS_CALLBACK_RECEIVE_CLIENT_HTTP = 46,
+ /**< This indicates data was received on the HTTP client connection. It
+ * does NOT actually drain or provide the data, so if you are doing
+ * http client, you MUST handle this and call lws_http_client_read().
+ * Failure to deal with it as in the minimal examples may cause spinning
+ * around the event loop as it's continuously signalled the same data
+ * is available for read. The related minimal examples show how to
+ * handle it.
+ *
+ * It's possible to defer calling lws_http_client_read() if you use
+ * rx flow control to stop further rx handling on the connection until
+ * you did deal with it. But normally you would call it in the handler.
+ *
+ * lws_http_client_read() strips any chunked framing and calls back
+ * with only payload data to LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ. The
+ * chunking is the reason this is not just all done in one callback for
+ * http.
+ */
+ LWS_CALLBACK_COMPLETED_CLIENT_HTTP = 47,
+ /**< The client transaction completed... at the moment this
+ * is the same as closing since transaction pipelining on
+ * client side is not yet supported. */
+
+ LWS_CALLBACK_CLIENT_HTTP_WRITEABLE = 57,
+ /**< when doing an HTTP type client connection, you can call
+ * lws_client_http_body_pending(wsi, 1) from
+ * LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER to get these callbacks
+ * sending the HTTP headers.
+ *
+ * From this callback, when you have sent everything, you should let
+ * lws know by calling lws_client_http_body_pending(wsi, 0)
+ */
+
+ LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL = 85,
+ LWS_CALLBACK_CLIENT_HTTP_DROP_PROTOCOL = 76,
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to Websocket Server -----
+ */
+
+ LWS_CALLBACK_ESTABLISHED = 0,
+ /**< (VH) after the server completes a handshake with an incoming
+ * client. If you built the library with ssl support, in is a
+ * pointer to the ssl struct associated with the connection or NULL.
+ *
+ * b0 of len is set if the connection was made using ws-over-h2
+ */
+
+ LWS_CALLBACK_CLOSED = 4,
+ /**< when the websocket session ends */
+
+ LWS_CALLBACK_SERVER_WRITEABLE = 11,
+ /**< See LWS_CALLBACK_CLIENT_WRITEABLE */
+
+ LWS_CALLBACK_RECEIVE = 6,
+ /**< data has appeared for this server endpoint from a
+ * remote client, it can be found at *in and is
+ * len bytes long */
+
+ LWS_CALLBACK_RECEIVE_PONG = 7,
+ /**< servers receive PONG packets with this callback reason */
+
+ LWS_CALLBACK_WS_PEER_INITIATED_CLOSE = 38,
+ /**< The peer has sent an unsolicited Close WS packet. in and
+ * len are the optional close code (first 2 bytes, network
+ * order) and the optional additional information which is not
+ * defined in the standard, and may be a string or non human-readable
+ * data.
+ * If you return 0 lws will echo the close and then close the
+ * connection. If you return nonzero lws will just close the
+ * connection. */
+
+ LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION = 20,
+ /**< called when the handshake has
+ * been received and parsed from the client, but the response is
+ * not sent yet. Return non-zero to disallow the connection.
+ * user is a pointer to the connection user space allocation,
+ * in is the requested protocol name
+ * In your handler you can use the public APIs
+ * lws_hdr_total_length() / lws_hdr_copy() to access all of the
+ * headers using the header enums lws_token_indexes from
+ * libwebsockets.h to check for and read the supported header
+ * presence and content before deciding to allow the handshake
+ * to proceed or to kill the connection. */
+
+ LWS_CALLBACK_CONFIRM_EXTENSION_OKAY = 25,
+ /**< When the server handshake code
+ * sees that it does support a requested extension, before
+ * accepting the extension by additing to the list sent back to
+ * the client it gives this callback just to check that it's okay
+ * to use that extension. It calls back to the requested protocol
+ * and with in being the extension name, len is 0 and user is
+ * valid. Note though at this time the ESTABLISHED callback hasn't
+ * happened yet so if you initialize user content there, user
+ * content during this callback might not be useful for anything. */
+
+ LWS_CALLBACK_WS_SERVER_BIND_PROTOCOL = 77,
+ LWS_CALLBACK_WS_SERVER_DROP_PROTOCOL = 78,
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to Websocket Client -----
+ */
+
+ LWS_CALLBACK_CLIENT_CONNECTION_ERROR = 1,
+ /**< the request client connection has been unable to complete a
+ * handshake with the remote server. If in is non-NULL, you can
+ * find an error string of length len where it points to
+ *
+ * Diagnostic strings that may be returned include
+ *
+ * "getaddrinfo (ipv6) failed"
+ * "unknown address family"
+ * "getaddrinfo (ipv4) failed"
+ * "set socket opts failed"
+ * "insert wsi failed"
+ * "lws_ssl_client_connect1 failed"
+ * "lws_ssl_client_connect2 failed"
+ * "Peer hung up"
+ * "read failed"
+ * "HS: URI missing"
+ * "HS: Redirect code but no Location"
+ * "HS: URI did not parse"
+ * "HS: Redirect failed"
+ * "HS: Server did not return 200"
+ * "HS: OOM"
+ * "HS: disallowed by client filter"
+ * "HS: disallowed at ESTABLISHED"
+ * "HS: ACCEPT missing"
+ * "HS: ws upgrade response not 101"
+ * "HS: UPGRADE missing"
+ * "HS: Upgrade to something other than websocket"
+ * "HS: CONNECTION missing"
+ * "HS: UPGRADE malformed"
+ * "HS: PROTOCOL malformed"
+ * "HS: Cannot match protocol"
+ * "HS: EXT: list too big"
+ * "HS: EXT: failed setting defaults"
+ * "HS: EXT: failed parsing defaults"
+ * "HS: EXT: failed parsing options"
+ * "HS: EXT: Rejects server options"
+ * "HS: EXT: unknown ext"
+ * "HS: Accept hash wrong"
+ * "HS: Rejected by filter cb"
+ * "HS: OOM"
+ * "HS: SO_SNDBUF failed"
+ * "HS: Rejected at CLIENT_ESTABLISHED"
+ */
+
+ LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH = 2,
+ /**< this is the last chance for the client user code to examine the
+ * http headers and decide to reject the connection. If the
+ * content in the headers is interesting to the
+ * client (url, etc) it needs to copy it out at
+ * this point since it will be destroyed before
+ * the CLIENT_ESTABLISHED call */
+
+ LWS_CALLBACK_CLIENT_ESTABLISHED = 3,
+ /**< after your client connection completed the websocket upgrade
+ * handshake with the remote server */
+
+ LWS_CALLBACK_CLIENT_CLOSED = 75,
+ /**< when a client websocket session ends */
+
+ LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER = 24,
+ /**< this callback happens
+ * when a client handshake is being compiled. user is NULL,
+ * in is a char **, it's pointing to a char * which holds the
+ * next location in the header buffer where you can add
+ * headers, and len is the remaining space in the header buffer,
+ * which is typically some hundreds of bytes. So, to add a canned
+ * cookie, your handler code might look similar to:
+ *
+ * char **p = (char **)in, *end = (*p) + len;
+ *
+ * if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_COOKIE,
+ * (unsigned char)"a=b", 3, p, end))
+ * return -1;
+ *
+ * See LWS_CALLBACK_ADD_HEADERS for adding headers to server
+ * transactions.
+ */
+
+ LWS_CALLBACK_CLIENT_RECEIVE = 8,
+ /**< data has appeared from the server for the client connection, it
+ * can be found at *in and is len bytes long */
+
+ LWS_CALLBACK_CLIENT_RECEIVE_PONG = 9,
+ /**< clients receive PONG packets with this callback reason */
+
+ LWS_CALLBACK_CLIENT_WRITEABLE = 10,
+ /**< If you call lws_callback_on_writable() on a connection, you will
+ * get one of these callbacks coming when the connection socket
+ * is able to accept another write packet without blocking.
+ * If it already was able to take another packet without blocking,
+ * you'll get this callback at the next call to the service loop
+ * function. Notice that CLIENTs get LWS_CALLBACK_CLIENT_WRITEABLE
+ * and servers get LWS_CALLBACK_SERVER_WRITEABLE. */
+
+ LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED = 26,
+ /**< When a ws client
+ * connection is being prepared to start a handshake to a server,
+ * each supported extension is checked with protocols[0] callback
+ * with this reason, giving the user code a chance to suppress the
+ * claim to support that extension by returning non-zero. If
+ * unhandled, by default 0 will be returned and the extension
+ * support included in the header to the server. Notice this
+ * callback comes to protocols[0]. */
+
+ LWS_CALLBACK_WS_EXT_DEFAULTS = 39,
+ /**< Gives client connections an opportunity to adjust negotiated
+ * extension defaults. `user` is the extension name that was
+ * negotiated (eg, "permessage-deflate"). `in` points to a
+ * buffer and `len` is the buffer size. The user callback can
+ * set the buffer to a string describing options the extension
+ * should parse. Or just ignore for defaults. */
+
+
+ LWS_CALLBACK_FILTER_NETWORK_CONNECTION = 17,
+ /**< called when a client connects to
+ * the server at network level; the connection is accepted but then
+ * passed to this callback to decide whether to hang up immediately
+ * or not, based on the client IP. in contains the connection
+ * socket's descriptor. Since the client connection information is
+ * not available yet, wsi still pointing to the main server socket.
+ * Return non-zero to terminate the connection before sending or
+ * receiving anything. Because this happens immediately after the
+ * network connection from the client, there's no websocket protocol
+ * selected yet so this callback is issued only to protocol 0. */
+
+ LWS_CALLBACK_WS_CLIENT_BIND_PROTOCOL = 79,
+ LWS_CALLBACK_WS_CLIENT_DROP_PROTOCOL = 80,
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to external poll loop integration -----
+ */
+
+ LWS_CALLBACK_GET_THREAD_ID = 31,
+ /**< lws can accept callback when writable requests from other
+ * threads, if you implement this callback and return an opaque
+ * current thread ID integer. */
+
+ /* external poll() management support */
+ LWS_CALLBACK_ADD_POLL_FD = 32,
+ /**< lws normally deals with its poll() or other event loop
+ * internally, but in the case you are integrating with another
+ * server you will need to have lws sockets share a
+ * polling array with the other server. This and the other
+ * POLL_FD related callbacks let you put your specialized
+ * poll array interface code in the callback for protocol 0, the
+ * first protocol you support, usually the HTTP protocol in the
+ * serving case.
+ * This callback happens when a socket needs to be
+ * added to the polling loop: in points to a struct
+ * lws_pollargs; the fd member of the struct is the file
+ * descriptor, and events contains the active events
+ *
+ * If you are using the internal lws polling / event loop
+ * you can just ignore these callbacks. */
+
+ LWS_CALLBACK_DEL_POLL_FD = 33,
+ /**< This callback happens when a socket descriptor
+ * needs to be removed from an external polling array. in is
+ * again the struct lws_pollargs containing the fd member
+ * to be removed. If you are using the internal polling
+ * loop, you can just ignore it. */
+
+ LWS_CALLBACK_CHANGE_MODE_POLL_FD = 34,
+ /**< This callback happens when lws wants to modify the events for
+ * a connection.
+ * in is the struct lws_pollargs with the fd to change.
+ * The new event mask is in events member and the old mask is in
+ * the prev_events member.
+ * If you are using the internal polling loop, you can just ignore
+ * it. */
+
+ LWS_CALLBACK_LOCK_POLL = 35,
+ /**< These allow the external poll changes driven
+ * by lws to participate in an external thread locking
+ * scheme around the changes, so the whole thing is threadsafe.
+ * These are called around three activities in the library,
+ * - inserting a new wsi in the wsi / fd table (len=1)
+ * - deleting a wsi from the wsi / fd table (len=1)
+ * - changing a wsi's POLLIN/OUT state (len=0)
+ * Locking and unlocking external synchronization objects when
+ * len == 1 allows external threads to be synchronized against
+ * wsi lifecycle changes if it acquires the same lock for the
+ * duration of wsi dereference from the other thread context. */
+
+ LWS_CALLBACK_UNLOCK_POLL = 36,
+ /**< See LWS_CALLBACK_LOCK_POLL, ignore if using lws internal poll */
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to CGI serving -----
+ */
+
+ LWS_CALLBACK_CGI = 40,
+ /**< CGI: CGI IO events on stdin / out / err are sent here on
+ * protocols[0]. The provided `lws_callback_http_dummy()`
+ * handles this and the callback should be directed there if
+ * you use CGI. */
+
+ LWS_CALLBACK_CGI_TERMINATED = 41,
+ /**< CGI: The related CGI process ended, this is called before
+ * the wsi is closed. Used to, eg, terminate chunking.
+ * The provided `lws_callback_http_dummy()`
+ * handles this and the callback should be directed there if
+ * you use CGI. The child PID that terminated is in len. */
+
+ LWS_CALLBACK_CGI_STDIN_DATA = 42,
+ /**< CGI: Data is, to be sent to the CGI process stdin, eg from
+ * a POST body. The provided `lws_callback_http_dummy()`
+ * handles this and the callback should be directed there if
+ * you use CGI. */
+
+ LWS_CALLBACK_CGI_STDIN_COMPLETED = 43,
+ /**< CGI: no more stdin is coming. The provided
+ * `lws_callback_http_dummy()` handles this and the callback
+ * should be directed there if you use CGI. */
+
+ LWS_CALLBACK_CGI_PROCESS_ATTACH = 70,
+ /**< CGI: Sent when the CGI process is spawned for the wsi. The
+ * len parameter is the PID of the child process */
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to Generic Sessions -----
+ */
+
+ LWS_CALLBACK_SESSION_INFO = 54,
+ /**< This is only generated by user code using generic sessions.
+ * It's used to get a `struct lws_session_info` filled in by
+ * generic sessions with information about the logged-in user.
+ * See the messageboard sample for an example of how to use. */
+
+ LWS_CALLBACK_GS_EVENT = 55,
+ /**< Indicates an event happened to the Generic Sessions session.
+ * `in` contains a `struct lws_gs_event_args` describing the event. */
+
+ LWS_CALLBACK_HTTP_PMO = 56,
+ /**< per-mount options for this connection, called before
+ * the normal LWS_CALLBACK_HTTP when the mount has per-mount
+ * options.
+ */
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to RAW PROXY -----
+ */
+
+ LWS_CALLBACK_RAW_PROXY_CLI_RX = 89,
+ /**< RAW mode client (outgoing) RX */
+
+ LWS_CALLBACK_RAW_PROXY_SRV_RX = 90,
+ /**< RAW mode server (listening) RX */
+
+ LWS_CALLBACK_RAW_PROXY_CLI_CLOSE = 91,
+ /**< RAW mode client (outgoing) is closing */
+
+ LWS_CALLBACK_RAW_PROXY_SRV_CLOSE = 92,
+ /**< RAW mode server (listening) is closing */
+
+ LWS_CALLBACK_RAW_PROXY_CLI_WRITEABLE = 93,
+ /**< RAW mode client (outgoing) may be written */
+
+ LWS_CALLBACK_RAW_PROXY_SRV_WRITEABLE = 94,
+ /**< RAW mode server (listening) may be written */
+
+ LWS_CALLBACK_RAW_PROXY_CLI_ADOPT = 95,
+ /**< RAW mode client (onward) accepted socket was adopted
+ * (equivalent to 'wsi created') */
+
+ LWS_CALLBACK_RAW_PROXY_SRV_ADOPT = 96,
+ /**< RAW mode server (listening) accepted socket was adopted
+ * (equivalent to 'wsi created') */
+
+ LWS_CALLBACK_RAW_PROXY_CLI_BIND_PROTOCOL = 97,
+ LWS_CALLBACK_RAW_PROXY_SRV_BIND_PROTOCOL = 98,
+ LWS_CALLBACK_RAW_PROXY_CLI_DROP_PROTOCOL = 99,
+ LWS_CALLBACK_RAW_PROXY_SRV_DROP_PROTOCOL = 100,
+
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to RAW sockets -----
+ */
+
+ LWS_CALLBACK_RAW_RX = 59,
+ /**< RAW mode connection RX */
+
+ LWS_CALLBACK_RAW_CLOSE = 60,
+ /**< RAW mode connection is closing */
+
+ LWS_CALLBACK_RAW_WRITEABLE = 61,
+ /**< RAW mode connection may be written */
+
+ LWS_CALLBACK_RAW_ADOPT = 62,
+ /**< RAW mode connection was adopted (equivalent to 'wsi created') */
+
+ LWS_CALLBACK_RAW_CONNECTED = 101,
+ /**< outgoing client RAW mode connection was connected */
+
+ LWS_CALLBACK_RAW_SKT_BIND_PROTOCOL = 81,
+ LWS_CALLBACK_RAW_SKT_DROP_PROTOCOL = 82,
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to RAW file handles -----
+ */
+
+ LWS_CALLBACK_RAW_ADOPT_FILE = 63,
+ /**< RAW mode file was adopted (equivalent to 'wsi created') */
+
+ LWS_CALLBACK_RAW_RX_FILE = 64,
+ /**< This is the indication the RAW mode file has something to read.
+ * This doesn't actually do the read of the file and len is always
+ * 0... your code should do the read having been informed there is
+ * something to read now. */
+
+ LWS_CALLBACK_RAW_WRITEABLE_FILE = 65,
+ /**< RAW mode file is writeable */
+
+ LWS_CALLBACK_RAW_CLOSE_FILE = 66,
+ /**< RAW mode wsi that adopted a file is closing */
+
+ LWS_CALLBACK_RAW_FILE_BIND_PROTOCOL = 83,
+ LWS_CALLBACK_RAW_FILE_DROP_PROTOCOL = 84,
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to generic wsi events -----
+ */
+
+ LWS_CALLBACK_TIMER = 73,
+ /**< When the time elapsed after a call to
+ * lws_set_timer_usecs(wsi, usecs) is up, the wsi will get one of
+ * these callbacks. The deadline can be continuously extended into the
+ * future by later calls to lws_set_timer_usecs() before the deadline
+ * expires, or cancelled by lws_set_timer_usecs(wsi, -1);
+ */
+
+ LWS_CALLBACK_EVENT_WAIT_CANCELLED = 71,
+ /**< This is sent to every protocol of every vhost in response
+ * to lws_cancel_service() or lws_cancel_service_pt(). This
+ * callback is serialized in the lws event loop normally, even
+ * if the lws_cancel_service[_pt]() call was from a different
+ * thread. */
+
+ LWS_CALLBACK_CHILD_CLOSING = 69,
+ /**< Sent to parent to notify them a child is closing / being
+ * destroyed. in is the child wsi.
+ */
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to TLS certificate management -----
+ */
+
+ LWS_CALLBACK_VHOST_CERT_AGING = 72,
+ /**< When a vhost TLS cert has its expiry checked, this callback
+ * is broadcast to every protocol of every vhost in case the
+ * protocol wants to take some action with this information.
+ * \p in is a pointer to a struct lws_acme_cert_aging_args,
+ * and \p len is the number of days left before it expires, as
+ * a (ssize_t). In the struct lws_acme_cert_aging_args, vh
+ * points to the vhost the cert aging information applies to,
+ * and element_overrides[] is an optional way to update information
+ * from the pvos... NULL in an index means use the information from
+ * from the pvo for the cert renewal, non-NULL in the array index
+ * means use that pointer instead for the index. */
+
+ LWS_CALLBACK_VHOST_CERT_UPDATE = 74,
+ /**< When a vhost TLS cert is being updated, progress is
+ * reported to the vhost in question here, including completion
+ * and failure. in points to optional JSON, and len represents the
+ * connection state using enum lws_cert_update_state */
+
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to MQTT Client -----
+ */
+
+ LWS_CALLBACK_MQTT_NEW_CLIENT_INSTANTIATED = 200,
+ LWS_CALLBACK_MQTT_IDLE = 201,
+ LWS_CALLBACK_MQTT_CLIENT_ESTABLISHED = 202,
+ LWS_CALLBACK_MQTT_SUBSCRIBED = 203,
+ LWS_CALLBACK_MQTT_CLIENT_WRITEABLE = 204,
+ LWS_CALLBACK_MQTT_CLIENT_RX = 205,
+ LWS_CALLBACK_MQTT_UNSUBSCRIBED = 206,
+ LWS_CALLBACK_MQTT_DROP_PROTOCOL = 207,
+ LWS_CALLBACK_MQTT_CLIENT_CLOSED = 208,
+ LWS_CALLBACK_MQTT_ACK = 209,
+ /**< When a message is fully sent, if QoS0 this callback is generated
+ * to locally "acknowledge" it. For QoS1, this callback is only
+ * generated when the matching PUBACK is received. Return nonzero to
+ * close the wsi.
+ */
+ LWS_CALLBACK_MQTT_RESEND = 210,
+ /**< In QoS1, this callback is generated instead of the _ACK one if
+ * we timed out waiting for a PUBACK and we must resend the message.
+ * Return nonzero to close the wsi.
+ */
+
+ /****** add new things just above ---^ ******/
+
+ LWS_CALLBACK_USER = 1000,
+ /**< user code can use any including above without fear of clashes */
+};
+
+
+
+/**
+ * typedef lws_callback_function() - User server actions
+ * \param wsi: Opaque websocket instance pointer
+ * \param reason: The reason for the call
+ * \param user: Pointer to per-session user data allocated by library
+ * \param in: Pointer used for some callback reasons
+ * \param len: Length set for some callback reasons
+ *
+ * This callback is the way the user controls what is served. All the
+ * protocol detail is hidden and handled by the library.
+ *
+ * For each connection / session there is user data allocated that is
+ * pointed to by "user". You set the size of this user data area when
+ * the library is initialized with lws_create_server.
+ */
+typedef int
+lws_callback_function(struct lws *wsi, enum lws_callback_reasons reason,
+ void *user, void *in, size_t len);
+
+#define LWS_CB_REASON_AUX_BF__CGI 1
+#define LWS_CB_REASON_AUX_BF__PROXY 2
+#define LWS_CB_REASON_AUX_BF__CGI_CHUNK_END 4
+#define LWS_CB_REASON_AUX_BF__CGI_HEADERS 8
+#define LWS_CB_REASON_AUX_BF__PROXY_TRANS_END 16
+#define LWS_CB_REASON_AUX_BF__PROXY_HEADERS 32
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*! \defgroup cgi cgi handling
+ *
+ * ##CGI handling
+ *
+ * These functions allow low-level control over stdin/out/err of the cgi.
+ *
+ * However for most cases, binding the cgi to http in and out, the default
+ * lws implementation already does the right thing.
+ */
+
+enum lws_enum_stdinouterr {
+ LWS_STDIN = 0,
+ LWS_STDOUT = 1,
+ LWS_STDERR = 2,
+};
+
+enum lws_cgi_hdr_state {
+ LCHS_HEADER,
+ LCHS_CR1,
+ LCHS_LF1,
+ LCHS_CR2,
+ LCHS_LF2,
+ LHCS_RESPONSE,
+ LHCS_DUMP_HEADERS,
+ LHCS_PAYLOAD,
+ LCHS_SINGLE_0A,
+};
+
+struct lws_cgi_args {
+ struct lws **stdwsi; /**< get fd with lws_get_socket_fd() */
+ enum lws_enum_stdinouterr ch; /**< channel index */
+ unsigned char *data; /**< for messages with payload */
+ enum lws_cgi_hdr_state hdr_state; /**< track where we are in cgi headers */
+ int len; /**< length */
+};
+
+#ifdef LWS_WITH_CGI
+/**
+ * lws_cgi: spawn network-connected cgi process
+ *
+ * \param wsi: connection to own the process
+ * \param exec_array: array of "exec-name" "arg1" ... "argn" NULL
+ * \param script_uri_path_len: how many chars on the left of the uri are the
+ * path to the cgi, or -1 to spawn without URL-related env vars
+ * \param timeout_secs: seconds script should be allowed to run
+ * \param mp_cgienv: pvo list with per-vhost cgi options to put in env
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_cgi(struct lws *wsi, const char * const *exec_array,
+ int script_uri_path_len, int timeout_secs,
+ const struct lws_protocol_vhost_options *mp_cgienv);
+
+/**
+ * lws_cgi_write_split_stdout_headers: write cgi output accounting for header part
+ *
+ * \param wsi: connection to own the process
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_cgi_write_split_stdout_headers(struct lws *wsi);
+
+/**
+ * lws_cgi_kill: terminate cgi process associated with wsi
+ *
+ * \param wsi: connection to own the process
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_cgi_kill(struct lws *wsi);
+
+/**
+ * lws_cgi_get_stdwsi: get wsi for stdin, stdout, or stderr
+ *
+ * \param wsi: parent wsi that has cgi
+ * \param ch: which of LWS_STDIN, LWS_STDOUT or LWS_STDERR
+ */
+LWS_VISIBLE LWS_EXTERN struct lws *
+lws_cgi_get_stdwsi(struct lws *wsi, enum lws_enum_stdinouterr ch);
+
+#endif
+///@}
+
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*! \defgroup client Client related functions
+ * ##Client releated functions
+ * \ingroup lwsapi
+ *
+ * */
+///@{
+
+/** enum lws_client_connect_ssl_connection_flags - flags that may be used
+ * with struct lws_client_connect_info ssl_connection member to control if
+ * and how SSL checks apply to the client connection being created
+ */
+
+enum lws_client_connect_ssl_connection_flags {
+ LCCSCF_USE_SSL = (1 << 0),
+ LCCSCF_ALLOW_SELFSIGNED = (1 << 1),
+ LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK = (1 << 2),
+ LCCSCF_ALLOW_EXPIRED = (1 << 3),
+ LCCSCF_ALLOW_INSECURE = (1 << 4),
+ LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM = (1 << 5),
+ LCCSCF_H2_QUIRK_OVERFLOWS_TXCR = (1 << 6),
+ LCCSCF_H2_AUTH_BEARER = (1 << 7),
+ LCCSCF_H2_HEXIFY_AUTH_TOKEN = (1 << 8),
+ LCCSCF_H2_MANUAL_RXFLOW = (1 << 9),
+ LCCSCF_HTTP_MULTIPART_MIME = (1 << 10),
+ LCCSCF_HTTP_X_WWW_FORM_URLENCODED = (1 << 11),
+ LCCSCF_HTTP_NO_FOLLOW_REDIRECT = (1 << 12),
+
+ LCCSCF_PIPELINE = (1 << 16),
+ /**< Serialize / pipeline multiple client connections
+ * on a single connection where possible.
+ *
+ * HTTP/1.0: possible if Keep-Alive: yes sent by server
+ * HTTP/1.1: always possible... uses pipelining
+ * HTTP/2: always possible... uses parallel streams
+ */
+ LCCSCF_MUXABLE_STREAM = (1 << 17),
+};
+
+/** struct lws_client_connect_info - parameters to connect with when using
+ * lws_client_connect_via_info() */
+
+struct lws_client_connect_info {
+ struct lws_context *context;
+ /**< lws context to create connection in */
+ const char *address;
+ /**< remote address to connect to */
+ int port;
+ /**< remote port to connect to */
+ int ssl_connection;
+ /**< 0, or a combination of LCCSCF_ flags */
+ const char *path;
+ /**< uri path */
+ const char *host;
+ /**< content of host header */
+ const char *origin;
+ /**< content of origin header */
+ const char *protocol;
+ /**< list of ws protocols we could accept */
+ int ietf_version_or_minus_one;
+ /**< deprecated: currently leave at 0 or -1 */
+ void *userdata;
+ /**< if non-NULL, use this as wsi user_data instead of malloc it */
+ const void *client_exts;
+ /**< UNUSED... provide in info.extensions at context creation time */
+ const char *method;
+ /**< if non-NULL, do this http method instead of ws[s] upgrade.
+ * use "GET" to be a simple http client connection. "RAW" gets
+ * you a connected socket that lws itself will leave alone once
+ * connected. */
+ struct lws *parent_wsi;
+ /**< if another wsi is responsible for this connection, give it here.
+ * this is used to make sure if the parent closes so do any
+ * child connections first. */
+ const char *uri_replace_from;
+ /**< if non-NULL, when this string is found in URIs in
+ * text/html content-encoding, it's replaced with uri_replace_to */
+ const char *uri_replace_to;
+ /**< see uri_replace_from */
+ struct lws_vhost *vhost;
+ /**< vhost to bind to (used to determine related SSL_CTX) */
+ struct lws **pwsi;
+ /**< if not NULL, store the new wsi here early in the connection
+ * process. Although we return the new wsi, the call to create the
+ * client connection does progress the connection somewhat and may
+ * meet an error that will result in the connection being scrubbed and
+ * NULL returned. While the wsi exists though, he may process a
+ * callback like CLIENT_CONNECTION_ERROR with his wsi: this gives the
+ * user callback a way to identify which wsi it is that faced the error
+ * even before the new wsi is returned and even if ultimately no wsi
+ * is returned.
+ */
+ const char *iface;
+ /**< NULL to allow routing on any interface, or interface name or IP
+ * to bind the socket to */
+ const char *local_protocol_name;
+ /**< NULL: .protocol is used both to select the local protocol handler
+ * to bind to and as the list of remote ws protocols we could
+ * accept.
+ * non-NULL: this protocol name is used to bind the connection to
+ * the local protocol handler. .protocol is used for the
+ * list of remote ws protocols we could accept */
+ const char *alpn;
+ /**< NULL: allow lws default ALPN list, from vhost if present or from
+ * list of roles built into lws
+ * non-NULL: require one from provided comma-separated list of alpn
+ * tokens
+ */
+
+ struct lws_sequencer *seq;
+ /**< NULL, or an lws_seq_t that wants to be given messages about
+ * this wsi's lifecycle as it connects, errors or closes.
+ */
+
+ void *opaque_user_data;
+ /**< This data has no meaning to lws but is applied to the client wsi
+ * and can be retrieved by user code with lws_get_opaque_user_data().
+ * It's also provided with sequencer messages if the wsi is bound to
+ * an lws_seq_t.
+ */
+
+ const lws_retry_bo_t *retry_and_idle_policy;
+ /**< optional retry and idle policy to apply to this connection.
+ * Currently only the idle parts are applied to the connection.
+ */
+
+ int manual_initial_tx_credit;
+ /**< if LCCSCF_H2_MANUAL_REFLOW is set, this becomes the initial tx
+ * credit for the stream.
+ */
+
+ uint8_t sys_tls_client_cert;
+ /**< 0 means no client cert. 1+ means apply lws_system client cert 0+
+ * to the client connection.
+ */
+
+#if defined(LWS_ROLE_MQTT)
+ const lws_mqtt_client_connect_param_t *mqtt_cp;
+#else
+ void *mqtt_cp;
+#endif
+
+ /* Add new things just above here ---^
+ * This is part of the ABI, don't needlessly break compatibility
+ *
+ * The below is to ensure later library versions with new
+ * members added above will see 0 (default) even if the app
+ * was not built against the newer headers.
+ */
+
+ void *_unused[4]; /**< dummy */
+};
+
+/**
+ * lws_client_connect_via_info() - Connect to another websocket server
+ * \param ccinfo: pointer to lws_client_connect_info struct
+ *
+ * This function creates a connection to a remote server using the
+ * information provided in ccinfo.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws *
+lws_client_connect_via_info(const struct lws_client_connect_info *ccinfo);
+
+/**
+ * lws_init_vhost_client_ssl() - also enable client SSL on an existing vhost
+ *
+ * \param info: client ssl related info
+ * \param vhost: which vhost to initialize client ssl operations on
+ *
+ * You only need to call this if you plan on using SSL client connections on
+ * the vhost. For non-SSL client connections, it's not necessary to call this.
+ *
+ * The following members of info are used during the call
+ *
+ * - options must have LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT set,
+ * otherwise the call does nothing
+ * - provided_client_ssl_ctx must be NULL to get a generated client
+ * ssl context, otherwise you can pass a prepared one in by setting it
+ * - ssl_cipher_list may be NULL or set to the client valid cipher list
+ * - ssl_ca_filepath may be NULL or client cert filepath
+ * - ssl_cert_filepath may be NULL or client cert filepath
+ * - ssl_private_key_filepath may be NULL or client cert private key
+ *
+ * You must create your vhost explicitly if you want to use this, so you have
+ * a pointer to the vhost. Create the context first with the option flag
+ * LWS_SERVER_OPTION_EXPLICIT_VHOSTS and then call lws_create_vhost() with
+ * the same info struct.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_init_vhost_client_ssl(const struct lws_context_creation_info *info,
+ struct lws_vhost *vhost);
+/**
+ * lws_http_client_read() - consume waiting received http client data
+ *
+ * \param wsi: client connection
+ * \param buf: pointer to buffer pointer - fill with pointer to your buffer
+ * \param len: pointer to chunk length - fill with max length of buffer
+ *
+ * This is called when the user code is notified client http data has arrived.
+ * The user code may choose to delay calling it to consume the data, for example
+ * waiting until an onward connection is writeable.
+ *
+ * For non-chunked connections, up to len bytes of buf are filled with the
+ * received content. len is set to the actual amount filled before return.
+ *
+ * For chunked connections, the linear buffer content contains the chunking
+ * headers and it cannot be passed in one lump. Instead, this function will
+ * call back LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ with in pointing to the
+ * chunk start and len set to the chunk length. There will be as many calls
+ * as there are chunks or partial chunks in the buffer.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_http_client_read(struct lws *wsi, char **buf, int *len);
+
+/**
+ * lws_http_client_http_response() - get last HTTP response code
+ *
+ * \param wsi: client connection
+ *
+ * Returns the last server response code, eg, 200 for client http connections.
+ *
+ * You should capture this during the LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP
+ * callback, because after that the memory reserved for storing the related
+ * headers is freed and this value is lost.
+ */
+LWS_VISIBLE LWS_EXTERN unsigned int
+lws_http_client_http_response(struct lws *wsi);
+
+/**
+ * lws_tls_client_vhost_extra_cert_mem() - add more certs to vh client tls ctx
+ *
+ * \param vh: the vhost to give more client certs to
+ * \param der: pointer to der format additional cert
+ * \param der_len: size in bytes of der
+ *
+ * After the vhost is created with one cert for client verification, you
+ * can add additional, eg, intermediate, certs to the client tls context
+ * of the vhost, for use with validating the incoming server cert(s).
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_tls_client_vhost_extra_cert_mem(struct lws_vhost *vh,
+ const uint8_t *der, size_t der_len);
+
+/**
+ * lws_client_http_body_pending() - control if client connection needs to send body
+ *
+ * \param wsi: client connection
+ * \param something_left_to_send: nonzero if need to send more body, 0 (default)
+ * if nothing more to send
+ *
+ * If you will send payload data with your HTTP client connection, eg, for POST,
+ * when you set the related http headers in
+ * LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER callback you should also call
+ * this API with something_left_to_send nonzero, and call
+ * lws_callback_on_writable(wsi);
+ *
+ * After sending the headers, lws will call your callback with
+ * LWS_CALLBACK_CLIENT_HTTP_WRITEABLE reason when writable. You can send the
+ * next part of the http body payload, calling lws_callback_on_writable(wsi);
+ * if there is more to come, or lws_client_http_body_pending(wsi, 0); to
+ * let lws know the last part is sent and the connection can move on.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_client_http_body_pending(struct lws *wsi, int something_left_to_send);
+
+/**
+ * lws_client_http_multipart() - issue appropriate multipart header or trailer
+ *
+ * \param wsi: client connection
+ * \param name: multipart header name field, or NULL if end of multipart
+ * \param filename: multipart header filename field, or NULL if none
+ * \param content_type: multipart header content-type part, or NULL if none
+ * \param p: pointer to position in buffer
+ * \param end: end of buffer
+ *
+ * This issues a multipart mime boundary, or terminator if name = NULL.
+ *
+ * Returns 0 if OK or nonzero if couldn't fit in buffer
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_client_http_multipart(struct lws *wsi, const char *name,
+ const char *filename, const char *content_type,
+ char **p, char *end);
+
+/**
+ * lws_http_basic_auth_gen() - helper to encode client basic auth string
+ *
+ * \param user: user name
+ * \param pw: password
+ * \param buf: where to store base64 result
+ * \param len: max usable size of buf
+ *
+ * Encodes a username and password in Basic Auth format for use with the
+ * Authorization header. On return, buf is filled with something like
+ * "Basic QWxhZGRpbjpPcGVuU2VzYW1l".
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_http_basic_auth_gen(const char *user, const char *pw, char *buf, size_t len);
+
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*! \defgroup context-and-vhost context and vhost related functions
+ * ##Context and Vhost releated functions
+ * \ingroup lwsapi
+ *
+ *
+ * LWS requires that there is one context, in which you may define multiple
+ * vhosts. Each vhost is a virtual host, with either its own listen port
+ * or sharing an existing one. Each vhost has its own SSL context that can
+ * be set up individually or left disabled.
+ *
+ * If you don't care about multiple "site" support, you can ignore it and
+ * lws will create a single default vhost at context creation time.
+ */
+///@{
+
+/*
+ * NOTE: These public enums are part of the abi. If you want to add one,
+ * add it at where specified so existing users are unaffected.
+ */
+
+
+#define LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT ((1ll << 1) | \
+ (1ll << 12))
+ /**< (VH) Don't allow the connection unless the client has a
+ * client cert that we recognize; provides
+ * LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT */
+#define LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME (1ll << 2)
+ /**< (CTX) Don't try to get the server's hostname */
+#define LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT ((1ll << 3) | \
+ (1ll << 12))
+ /**< (VH) Allow non-SSL (plaintext) connections on the same
+ * port as SSL is listening. If combined with
+ * LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS it will try to
+ * force http connections on an https listener (eg, http://x.com:443) to
+ * redirect to an explicit https connection (eg, https://x.com)
+ */
+#define LWS_SERVER_OPTION_LIBEV (1ll << 4)
+ /**< (CTX) Use libev event loop */
+#define LWS_SERVER_OPTION_DISABLE_IPV6 (1ll << 5)
+ /**< (VH) Disable IPV6 support */
+#define LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS (1ll << 6)
+ /**< (VH) Don't load OS CA certs, you will need to load your
+ * own CA cert(s) */
+#define LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED (1ll << 7)
+ /**< (VH) Accept connections with no valid Cert (eg, selfsigned) */
+#define LWS_SERVER_OPTION_VALIDATE_UTF8 (1ll << 8)
+ /**< (VH) Check UT-8 correctness */
+#define LWS_SERVER_OPTION_SSL_ECDH ((1ll << 9) | \
+ (1ll << 12))
+ /**< (VH) initialize ECDH ciphers */
+#define LWS_SERVER_OPTION_LIBUV (1ll << 10)
+ /**< (CTX) Use libuv event loop */
+#define LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS ((1ll << 11) |\
+ (1ll << 12))
+ /**< (VH) Use an http redirect to force the client to ask for https.
+ * Notice if your http server issues the STS header and the client has
+ * ever seen that, the client will fail the http connection before it
+ * can actually do the redirect.
+ *
+ * Combine with LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS to handle, eg,
+ * http://x.com:443 -> https://x.com
+ *
+ * (deprecated: use mount redirection) */
+#define LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT (1ll << 12)
+ /**< (CTX) Initialize the SSL library at all */
+#define LWS_SERVER_OPTION_EXPLICIT_VHOSTS (1ll << 13)
+ /**< (CTX) Only create the context when calling context
+ * create api, implies user code will create its own vhosts */
+#define LWS_SERVER_OPTION_UNIX_SOCK (1ll << 14)
+ /**< (VH) Use Unix socket */
+#define LWS_SERVER_OPTION_STS (1ll << 15)
+ /**< (VH) Send Strict Transport Security header, making
+ * clients subsequently go to https even if user asked for http */
+#define LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY (1ll << 16)
+ /**< (VH) Enable LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE to take effect */
+#define LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE (1ll << 17)
+ /**< (VH) if set, only ipv6 allowed on the vhost */
+#define LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN (1ll << 18)
+ /**< (CTX) Libuv only: Do not spin on SIGSEGV / SIGFPE. A segfault
+ * normally makes the lib spin so you can attach a debugger to it
+ * even if it happened without a debugger in place. You can disable
+ * that by giving this option.
+ */
+#define LWS_SERVER_OPTION_JUST_USE_RAW_ORIGIN (1ll << 19)
+ /**< For backwards-compatibility reasons, by default
+ * lws prepends "http://" to the origin you give in the client
+ * connection info struct. If you give this flag when you create
+ * the context, only the string you give in the client connect
+ * info for .origin (if any) will be used directly.
+ */
+#define LWS_SERVER_OPTION_FALLBACK_TO_RAW /* use below name */ (1ll << 20)
+#define LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG (1ll << 20)
+ /**< (VH) if invalid http is coming in the first line, then abandon
+ * trying to treat the connection as http, and belatedly apply the
+ * .listen_accept_role / .listen_accept_protocol info struct members to
+ * the connection. If they are NULL, for backwards-compatibility the
+ * connection is bound to "raw-skt" role, and in order of priority:
+ * 1) the vh protocol with a pvo named "raw", 2) the vh protocol with a
+ * pvo named "default", or 3) protocols[0].
+ *
+ * Must be combined with LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT
+ * to work with a socket listening with tls.
+ */
+
+#define LWS_SERVER_OPTION_LIBEVENT (1ll << 21)
+ /**< (CTX) Use libevent event loop */
+
+#define LWS_SERVER_OPTION_ONLY_RAW /* Use below name instead */ (1ll << 22)
+#define LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG (1ll << 22)
+ /**< (VH) All connections to this vhost / port are bound to the
+ * role and protocol given in .listen_accept_role /
+ * .listen_accept_protocol.
+ *
+ * If those explicit user-controlled names are NULL, for backwards-
+ * compatibility the connection is bound to "raw-skt" role, and in order
+ * of priority: 1) the vh protocol with a pvo named "raw", 2) the vh
+ * protocol with a pvo named "default", or 3) protocols[0].
+ *
+ * It's much preferred to specify the role + protocol using the
+ * .listen_accept_role and .listen_accept_protocol in the info struct.
+ */
+#define LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE (1ll << 23)
+ /**< (VH) Set to allow multiple listen sockets on one interface +
+ * address + port. The default is to strictly allow only one
+ * listen socket at a time. This is automatically selected if you
+ * have multiple service threads. Linux only.
+ */
+#define LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX (1ll << 24)
+ /**< (VH) Force setting up the vhost SSL_CTX, even though the user
+ * code doesn't explicitly provide a cert in the info struct. It
+ * implies the user code is going to provide a cert at the
+ * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS callback, which
+ * provides the vhost SSL_CTX * in the user parameter.
+ */
+#define LWS_SERVER_OPTION_SKIP_PROTOCOL_INIT (1ll << 25)
+ /**< (VH) You probably don't want this. It forces this vhost to not
+ * call LWS_CALLBACK_PROTOCOL_INIT on its protocols. It's used in the
+ * special case of a temporary vhost bound to a single protocol.
+ */
+#define LWS_SERVER_OPTION_IGNORE_MISSING_CERT (1ll << 26)
+ /**< (VH) Don't fail if the vhost TLS cert or key are missing, just
+ * continue. The vhost won't be able to serve anything, but if for
+ * example the ACME plugin was configured to fetch a cert, this lets
+ * you bootstrap your vhost from having no cert to start with.
+ */
+#define LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK (1ll << 27)
+ /**< (VH) On this vhost, if the connection is being upgraded, insist
+ * that there's a Host: header and that the contents match the vhost
+ * name + port (443 / 80 are assumed if no :port given based on if the
+ * connection is using TLS).
+ *
+ * By default, without this flag, on upgrade lws just checks that the
+ * Host: header was given without checking the contents... this is to
+ * allow lax hostname mappings like localhost / 127.0.0.1, and CNAME
+ * mappings like www.mysite.com / mysite.com
+ */
+#define LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE (1ll << 28)
+ /**< (VH) Send lws default HTTP headers recommended by Mozilla
+ * Observatory for security. This is a helper option that sends canned
+ * headers on each http response enabling a VERY strict Content Security
+ * Policy. The policy is so strict, for example it won't let the page
+ * run its own inline JS nor show images or take CSS from a different
+ * server. In many cases your JS only comes from your server as do the
+ * image sources and CSS, so that is what you want... attackers hoping
+ * to inject JS into your DOM are completely out of luck since even if
+ * they succeed, it will be rejected for execution by the browser
+ * according to the strict CSP. In other cases you have to deviate from
+ * the complete strictness, in which case don't use this flag: use the
+ * .headers member in the vhost init described in struct
+ * lws_context_creation_info instead to send the adapted headers
+ * yourself.
+ */
+
+#define LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER (1ll << 29)
+ /**< (VH) If you really want to allow HTTP connections on a tls
+ * listener, you can do it with this combined with
+ * LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT. But this is allowing
+ * accidental loss of the security assurances provided by tls depending
+ * on the client using http when he meant https... it's not
+ * recommended.
+ */
+#define LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND (1ll << 30)
+ /**< (VH) When instantiating a new vhost and the specified port is
+ * already in use, a null value shall be return to signal the error.
+ */
+
+#define LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW (1ll << 31)
+ /**< (VH) Indicates the connections using this vhost should ignore
+ * h2 WINDOW_UPDATE from broken peers and fix them up */
+
+#define LWS_SERVER_OPTION_VH_H2_HALF_CLOSED_LONG_POLL (1ll << 32)
+ /**< (VH) Tell the vhost to treat half-closed remote clients as
+ * entered into an immortal (ie, not subject to normal timeouts) long
+ * poll mode.
+ */
+
+#define LWS_SERVER_OPTION_GLIB (1ll << 33)
+ /**< (CTX) Use glib event loop */
+
+ /****** add new things just above ---^ ******/
+
+
+#define lws_check_opt(c, f) ((((uint64_t)c) & ((uint64_t)f)) == ((uint64_t)f))
+
+struct lws_plat_file_ops;
+struct lws_ss_policy;
+struct lws_ss_plugin;
+
+typedef int (*lws_context_ready_cb_t)(struct lws_context *context);
+
+/** struct lws_context_creation_info - parameters to create context and /or vhost with
+ *
+ * This is also used to create vhosts.... if LWS_SERVER_OPTION_EXPLICIT_VHOSTS
+ * is not given, then for backwards compatibility one vhost is created at
+ * context-creation time using the info from this struct.
+ *
+ * If LWS_SERVER_OPTION_EXPLICIT_VHOSTS is given, then no vhosts are created
+ * at the same time as the context, they are expected to be created afterwards.
+ */
+struct lws_context_creation_info {
+ int port;
+ /**< VHOST: Port to listen on. Use CONTEXT_PORT_NO_LISTEN to suppress
+ * listening for a client. Use CONTEXT_PORT_NO_LISTEN_SERVER if you are
+ * writing a server but you are using \ref sock-adopt instead of the
+ * built-in listener.
+ *
+ * You can also set port to 0, in which case the kernel will pick
+ * a random port that is not already in use. You can find out what
+ * port the vhost is listening on using lws_get_vhost_listen_port() */
+ const char *iface;
+ /**< VHOST: NULL to bind the listen socket to all interfaces, or the
+ * interface name, eg, "eth2"
+ * If options specifies LWS_SERVER_OPTION_UNIX_SOCK, this member is
+ * the pathname of a UNIX domain socket. you can use the UNIX domain
+ * sockets in abstract namespace, by prepending an at symbol to the
+ * socket name. */
+ const struct lws_protocols *protocols;
+ /**< VHOST: Array of structures listing supported protocols and a
+ * protocol-specific callback for each one. The list is ended with an
+ * entry that has a NULL callback pointer. SEE ALSO .pprotocols below,
+ * which gives an alternative way to provide an array of pointers to
+ * protocol structs. */
+ const struct lws_extension *extensions;
+ /**< VHOST: NULL or array of lws_extension structs listing the
+ * extensions this context supports. */
+ const struct lws_token_limits *token_limits;
+ /**< CONTEXT: NULL or struct lws_token_limits pointer which is
+ * initialized with a token length limit for each possible WSI_TOKEN_ */
+ const char *ssl_private_key_password;
+ /**< VHOST: NULL or the passphrase needed for the private key. (For
+ * backwards compatibility, this can also be used to pass the client
+ * cert passphrase when setting up a vhost client SSL context, but it is
+ * preferred to use .client_ssl_private_key_password for that.) */
+ const char *ssl_cert_filepath;
+ /**< VHOST: If libwebsockets was compiled to use ssl, and you want
+ * to listen using SSL, set to the filepath to fetch the
+ * server cert from, otherwise NULL for unencrypted. (For backwards
+ * compatibility, this can also be used to pass the client certificate
+ * when setting up a vhost client SSL context, but it is preferred to
+ * use .client_ssl_cert_filepath for that.)
+ *
+ * Notice you can alternatively set a single DER or PEM from a memory
+ * buffer as the vhost tls cert using \p server_ssl_cert_mem and
+ * \p server_ssl_cert_mem_len.
+ */
+ const char *ssl_private_key_filepath;
+ /**< VHOST: filepath to private key if wanting SSL mode;
+ * if this is set to NULL but ssl_cert_filepath is set, the
+ * OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY callback is called
+ * to allow setting of the private key directly via openSSL
+ * library calls. (For backwards compatibility, this can also be used
+ * to pass the client cert private key filepath when setting up a
+ * vhost client SSL context, but it is preferred to use
+ * .client_ssl_private_key_filepath for that.)
+ *
+ * Notice you can alternatively set a DER or PEM private key from a
+ * memory buffer as the vhost tls private key using
+ * \p server_ssl_private_key_mem and \p server_ssl_private_key_mem_len.
+ */
+ const char *ssl_ca_filepath;
+ /**< VHOST: CA certificate filepath or NULL. (For backwards
+ * compatibility, this can also be used to pass the client CA
+ * filepath when setting up a vhost client SSL context,
+ * but it is preferred to use .client_ssl_ca_filepath for that.)
+ *
+ * Notice you can alternatively set a DER or PEM CA cert from a memory
+ * buffer using \p server_ssl_ca_mem and \p server_ssl_ca_mem_len.
+ */
+ const char *ssl_cipher_list;
+ /**< VHOST: List of valid ciphers to use ON TLS1.2 AND LOWER ONLY (eg,
+ * "RC4-MD5:RC4-SHA:AES128-SHA:AES256-SHA:HIGH:!DSS:!aNULL"
+ * or you can leave it as NULL to get "DEFAULT" (For backwards
+ * compatibility, this can also be used to pass the client cipher
+ * list when setting up a vhost client SSL context,
+ * but it is preferred to use .client_ssl_cipher_list for that.)
+ * SEE .tls1_3_plus_cipher_list and .client_tls_1_3_plus_cipher_list
+ * for the equivalent for tls1.3.
+ */
+ const char *http_proxy_address;
+ /**< VHOST: If non-NULL, attempts to proxy via the given address.
+ * If proxy auth is required, use format
+ * "username:password\@server:port" */
+ unsigned int http_proxy_port;
+ /**< VHOST: If http_proxy_address was non-NULL, uses this port */
+ int gid;
+ /**< CONTEXT: group id to change to after setting listen socket,
+ * or -1. See also .username below. */
+ int uid;
+ /**< CONTEXT: user id to change to after setting listen socket,
+ * or -1. See also .groupname below. */
+ uint64_t options;
+ /**< VHOST + CONTEXT: 0, or LWS_SERVER_OPTION_... bitfields */
+ void *user;
+ /**< VHOST + CONTEXT: optional user pointer that will be associated
+ * with the context when creating the context (and can be retrieved by
+ * lws_context_user(context), or with the vhost when creating the vhost
+ * (and can be retrieved by lws_vhost_user(vhost)). You will need to
+ * use LWS_SERVER_OPTION_EXPLICIT_VHOSTS and create the vhost separately
+ * if you care about giving the context and vhost different user pointer
+ * values.
+ */
+ int ka_time;
+ /**< CONTEXT: 0 for no TCP keepalive, otherwise apply this keepalive
+ * timeout to all libwebsocket sockets, client or server */
+ int ka_probes;
+ /**< CONTEXT: if ka_time was nonzero, after the timeout expires how many
+ * times to try to get a response from the peer before giving up
+ * and killing the connection */
+ int ka_interval;
+ /**< CONTEXT: if ka_time was nonzero, how long to wait before each ka_probes
+ * attempt */
+#if defined(LWS_WITH_TLS) && !defined(LWS_WITH_MBEDTLS)
+ SSL_CTX *provided_client_ssl_ctx;
+ /**< CONTEXT: If non-null, swap out libwebsockets ssl
+ * implementation for the one provided by provided_ssl_ctx.
+ * Libwebsockets no longer is responsible for freeing the context
+ * if this option is selected. */
+#else /* maintain structure layout either way */
+ void *provided_client_ssl_ctx; /**< dummy if ssl disabled */
+#endif
+
+ unsigned short max_http_header_data;
+ /**< CONTEXT: The max amount of header payload that can be handled
+ * in an http request (unrecognized header payload is dropped) */
+ unsigned short max_http_header_pool;
+ /**< CONTEXT: The max number of connections with http headers that
+ * can be processed simultaneously (the corresponding memory is
+ * allocated and deallocated dynamically as needed). If the pool is
+ * fully busy new incoming connections must wait for accept until one
+ * becomes free. 0 = allow as many ah as number of availble fds for
+ * the process */
+
+ unsigned int count_threads;
+ /**< CONTEXT: how many contexts to create in an array, 0 = 1 */
+ unsigned int fd_limit_per_thread;
+ /**< CONTEXT: nonzero means restrict each service thread to this
+ * many fds, 0 means the default which is divide the process fd
+ * limit by the number of threads.
+ *
+ * Note if this is nonzero, and fd_limit_per_thread multiplied by the
+ * number of service threads is less than the process ulimit, then lws
+ * restricts internal lookup table allocation to the smaller size, and
+ * switches to a less efficient lookup scheme. You should use this to
+ * trade off speed against memory usage if you know the lws context
+ * will only use a handful of fds.
+ *
+ * Bear in mind lws may use some fds internally, for example for the
+ * cancel pipe, so you may need to allow for some extras for normal
+ * operation.
+ */
+ unsigned int timeout_secs;
+ /**< VHOST: various processes involving network roundtrips in the
+ * library are protected from hanging forever by timeouts. If
+ * nonzero, this member lets you set the timeout used in seconds.
+ * Otherwise a default timeout is used. */
+ const char *ecdh_curve;
+ /**< VHOST: if NULL, defaults to initializing server with
+ * "prime256v1" */
+ const char *vhost_name;
+ /**< VHOST: name of vhost, must match external DNS name used to
+ * access the site, like "warmcat.com" as it's used to match
+ * Host: header and / or SNI name for SSL. */
+ const char * const *plugin_dirs;
+ /**< CONTEXT: NULL, or NULL-terminated array of directories to
+ * scan for lws protocol plugins at context creation time */
+ const struct lws_protocol_vhost_options *pvo;
+ /**< VHOST: pointer to optional linked list of per-vhost
+ * options made accessible to protocols */
+ int keepalive_timeout;
+ /**< VHOST: (default = 0 = 5s, 31s for http/2) seconds to allow remote
+ * client to hold on to an idle HTTP/1.1 connection. Timeout lifetime
+ * applied to idle h2 network connections */
+ const char *log_filepath;
+ /**< VHOST: filepath to append logs to... this is opened before
+ * any dropping of initial privileges */
+ const struct lws_http_mount *mounts;
+ /**< VHOST: optional linked list of mounts for this vhost */
+ const char *server_string;
+ /**< CONTEXT: string used in HTTP headers to identify server
+ * software, if NULL, "libwebsockets". */
+ unsigned int pt_serv_buf_size;
+ /**< CONTEXT: 0 = default of 4096. This buffer is used by
+ * various service related features including file serving, it
+ * defines the max chunk of file that can be sent at once.
+ * At the risk of lws having to buffer failed large sends, it
+ * can be increased to, eg, 128KiB to improve throughput. */
+ unsigned int max_http_header_data2;
+ /**< CONTEXT: if max_http_header_data is 0 and this
+ * is nonzero, this will be used in place of the default. It's
+ * like this for compatibility with the original short version,
+ * this is unsigned int length. */
+ long ssl_options_set;
+ /**< VHOST: Any bits set here will be set as server SSL options */
+ long ssl_options_clear;
+ /**< VHOST: Any bits set here will be cleared as server SSL options */
+ unsigned short ws_ping_pong_interval;
+ /**< CONTEXT: 0 for none, else interval in seconds between sending
+ * PINGs on idle websocket connections. When the PING is sent,
+ * the PONG must come within the normal timeout_secs timeout period
+ * or the connection will be dropped.
+ * Any RX or TX traffic on the connection restarts the interval timer,
+ * so a connection which always sends or receives something at intervals
+ * less than the interval given here will never send PINGs / expect
+ * PONGs. Conversely as soon as the ws connection is established, an
+ * idle connection will do the PING / PONG roundtrip as soon as
+ * ws_ping_pong_interval seconds has passed without traffic
+ */
+ const struct lws_protocol_vhost_options *headers;
+ /**< VHOST: pointer to optional linked list of per-vhost
+ * canned headers that are added to server responses */
+
+ const struct lws_protocol_vhost_options *reject_service_keywords;
+ /**< CONTEXT: Optional list of keywords and rejection codes + text.
+ *
+ * The keywords are checked for existing in the user agent string.
+ *
+ * Eg, "badrobot" "404 Not Found"
+ */
+ void *external_baggage_free_on_destroy;
+ /**< CONTEXT: NULL, or pointer to something externally malloc'd, that
+ * should be freed when the context is destroyed. This allows you to
+ * automatically sync the freeing action to the context destruction
+ * action, so there is no need for an external free() if the context
+ * succeeded to create.
+ */
+
+ const char *client_ssl_private_key_password;
+ /**< VHOST: Client SSL context init: NULL or the passphrase needed
+ * for the private key */
+ const char *client_ssl_cert_filepath;
+ /**< VHOST: Client SSL context init: The certificate the client
+ * should present to the peer on connection */
+ const void *client_ssl_cert_mem;
+ /**< VHOST: Client SSL context init: client certificate memory buffer or
+ * NULL... use this to load client cert from memory instead of file */
+ unsigned int client_ssl_cert_mem_len;
+ /**< VHOST: Client SSL context init: length of client_ssl_cert_mem in
+ * bytes */
+ const char *client_ssl_private_key_filepath;
+ /**< VHOST: Client SSL context init: filepath to client private key
+ * if this is set to NULL but client_ssl_cert_filepath is set, you
+ * can handle the LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS
+ * callback of protocols[0] to allow setting of the private key directly
+ * via tls library calls */
+ const char *client_ssl_ca_filepath;
+ /**< VHOST: Client SSL context init: CA certificate filepath or NULL */
+ const void *client_ssl_ca_mem;
+ /**< VHOST: Client SSL context init: CA certificate memory buffer or
+ * NULL... use this to load CA cert from memory instead of file */
+ unsigned int client_ssl_ca_mem_len;
+ /**< VHOST: Client SSL context init: length of client_ssl_ca_mem in
+ * bytes */
+
+ const char *client_ssl_cipher_list;
+ /**< VHOST: Client SSL context init: List of valid ciphers to use (eg,
+ * "RC4-MD5:RC4-SHA:AES128-SHA:AES256-SHA:HIGH:!DSS:!aNULL"
+ * or you can leave it as NULL to get "DEFAULT" */
+
+ const struct lws_plat_file_ops *fops;
+ /**< CONTEXT: NULL, or pointer to an array of fops structs, terminated
+ * by a sentinel with NULL .open.
+ *
+ * If NULL, lws provides just the platform file operations struct for
+ * backwards compatibility.
+ */
+ int simultaneous_ssl_restriction;
+ /**< CONTEXT: 0 (no limit) or limit of simultaneous SSL sessions
+ * possible.*/
+ const char *socks_proxy_address;
+ /**< VHOST: If non-NULL, attempts to proxy via the given address.
+ * If proxy auth is required, use format
+ * "username:password\@server:port" */
+ unsigned int socks_proxy_port;
+ /**< VHOST: If socks_proxy_address was non-NULL, uses this port
+ * if nonzero, otherwise requires "server:port" in .socks_proxy_address
+ */
+#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
+ cap_value_t caps[4];
+ /**< CONTEXT: array holding Linux capabilities you want to
+ * continue to be available to the server after it transitions
+ * to a noprivileged user. Usually none are needed but for, eg,
+ * .bind_iface, CAP_NET_RAW is required. This gives you a way
+ * to still have the capability but drop root.
+ */
+ char count_caps;
+ /**< CONTEXT: count of Linux capabilities in .caps[]. 0 means
+ * no capabilities will be inherited from root (the default) */
+#endif
+ int bind_iface;
+ /**< VHOST: nonzero to strictly bind sockets to the interface name in
+ * .iface (eg, "eth2"), using SO_BIND_TO_DEVICE.
+ *
+ * Requires SO_BINDTODEVICE support from your OS and CAP_NET_RAW
+ * capability.
+ *
+ * Notice that common things like access network interface IP from
+ * your local machine use your lo / loopback interface and will be
+ * disallowed by this.
+ */
+ int ssl_info_event_mask;
+ /**< VHOST: mask of ssl events to be reported on LWS_CALLBACK_SSL_INFO
+ * callback for connections on this vhost. The mask values are of
+ * the form SSL_CB_ALERT, defined in openssl/ssl.h. The default of
+ * 0 means no info events will be reported.
+ */
+ unsigned int timeout_secs_ah_idle;
+ /**< VHOST: seconds to allow a client to hold an ah without using it.
+ * 0 defaults to 10s. */
+ unsigned short ip_limit_ah;
+ /**< CONTEXT: max number of ah a single IP may use simultaneously
+ * 0 is no limit. This is a soft limit: if the limit is
+ * reached, connections from that IP will wait in the ah
+ * waiting list and not be able to acquire an ah until
+ * a connection belonging to the IP relinquishes one it
+ * already has.
+ */
+ unsigned short ip_limit_wsi;
+ /**< CONTEXT: max number of wsi a single IP may use simultaneously.
+ * 0 is no limit. This is a hard limit, connections from
+ * the same IP will simply be dropped once it acquires the
+ * amount of simultaneous wsi / accepted connections
+ * given here.
+ */
+ uint32_t http2_settings[7];
+ /**< VHOST: if http2_settings[0] is nonzero, the values given in
+ * http2_settings[1]..[6] are used instead of the lws
+ * platform default values.
+ * Just leave all at 0 if you don't care.
+ */
+ const char *error_document_404;
+ /**< VHOST: If non-NULL, when asked to serve a non-existent file,
+ * lws attempts to server this url path instead. Eg,
+ * "/404.html" */
+ const char *alpn;
+ /**< CONTEXT: If non-NULL, default list of advertised alpn, comma-
+ * separated
+ *
+ * VHOST: If non-NULL, per-vhost list of advertised alpn, comma-
+ * separated
+ */
+ void **foreign_loops;
+ /**< CONTEXT: This is ignored if the context is not being started with
+ * an event loop, ie, .options has a flag like
+ * LWS_SERVER_OPTION_LIBUV.
+ *
+ * NULL indicates lws should start its own even loop for
+ * each service thread, and deal with closing the loops
+ * when the context is destroyed.
+ *
+ * Non-NULL means it points to an array of external
+ * ("foreign") event loops that are to be used in turn for
+ * each service thread. In the default case of 1 service
+ * thread, it can just point to one foreign event loop.
+ */
+ void (*signal_cb)(void *event_lib_handle, int signum);
+ /**< CONTEXT: NULL: default signal handling. Otherwise this receives
+ * the signal handler callback. event_lib_handle is the
+ * native event library signal handle, eg uv_signal_t *
+ * for libuv.
+ */
+ struct lws_context **pcontext;
+ /**< CONTEXT: if non-NULL, at the end of context destroy processing,
+ * the pointer pointed to by pcontext is written with NULL. You can
+ * use this to let foreign event loops know that lws context destruction
+ * is fully completed.
+ */
+ void (*finalize)(struct lws_vhost *vh, void *arg);
+ /**< VHOST: NULL, or pointer to function that will be called back
+ * when the vhost is just about to be freed. The arg parameter
+ * will be set to whatever finalize_arg is below.
+ */
+ void *finalize_arg;
+ /**< VHOST: opaque pointer lws ignores but passes to the finalize
+ * callback. If you don't care, leave it NULL.
+ */
+ unsigned int max_http_header_pool2;
+ /**< CONTEXT: if max_http_header_pool is 0 and this
+ * is nonzero, this will be used in place of the default. It's
+ * like this for compatibility with the original short version:
+ * this is unsigned int length. */
+
+ long ssl_client_options_set;
+ /**< VHOST: Any bits set here will be set as CLIENT SSL options */
+ long ssl_client_options_clear;
+ /**< VHOST: Any bits set here will be cleared as CLIENT SSL options */
+
+ const char *tls1_3_plus_cipher_list;
+ /**< VHOST: List of valid ciphers to use for incoming server connections
+ * ON TLS1.3 AND ABOVE (eg, "TLS_CHACHA20_POLY1305_SHA256" on this vhost
+ * or you can leave it as NULL to get "DEFAULT".
+ * SEE .client_tls_1_3_plus_cipher_list to do the same on the vhost
+ * client SSL_CTX.
+ */
+ const char *client_tls_1_3_plus_cipher_list;
+ /**< VHOST: List of valid ciphers to use for outgoing client connections
+ * ON TLS1.3 AND ABOVE on this vhost (eg,
+ * "TLS_CHACHA20_POLY1305_SHA256") or you can leave it as NULL to get
+ * "DEFAULT".
+ */
+ const char *listen_accept_role;
+ /**< VHOST: NULL for default, or force accepted incoming connections to
+ * bind to this role. Uses the role names from their ops struct, eg,
+ * "raw-skt".
+ */
+ const char *listen_accept_protocol;
+ /**< VHOST: NULL for default, or force accepted incoming connections to
+ * bind to this vhost protocol name.
+ */
+ const struct lws_protocols **pprotocols;
+ /**< VHOST: NULL: use .protocols, otherwise ignore .protocols and use
+ * this array of pointers to protocols structs. The end of the array
+ * is marked by a NULL pointer.
+ *
+ * This is preferred over .protocols, because it allows the protocol
+ * struct to be opaquely defined elsewhere, with just a pointer to it
+ * needed to create the context with it. .protocols requires also
+ * the type of the user data to be known so its size can be given.
+ */
+
+ const void *server_ssl_cert_mem;
+ /**< VHOST: Alternative for \p ssl_cert_filepath that allows setting
+ * from memory instead of from a file. At most one of
+ * \p ssl_cert_filepath or \p server_ssl_cert_mem should be non-NULL. */
+ unsigned int server_ssl_cert_mem_len;
+ /**< VHOST: Server SSL context init: length of server_ssl_cert_mem in
+ * bytes */
+ const void *server_ssl_private_key_mem;
+ /**< VHOST: Alternative for \p ssl_private_key_filepath allowing
+ * init from a private key in memory instead of a file. At most one
+ * of \p ssl_private_key_filepath or \p server_ssl_private_key_mem
+ * should be non-NULL. */
+ unsigned int server_ssl_private_key_mem_len;
+ /**< VHOST: length of \p server_ssl_private_key_mem in memory */
+ const void *server_ssl_ca_mem;
+ /**< VHOST: Alternative for \p ssl_ca_filepath allowing
+ * init from a CA cert in memory instead of a file. At most one
+ * of \p ssl_ca_filepath or \p server_ssl_ca_mem should be non-NULL. */
+ unsigned int server_ssl_ca_mem_len;
+ /**< VHOST: length of \p server_ssl_ca_mem in memory */
+ const char *username; /**< CONTEXT: string username for post-init
+ * permissions. Like .uid but takes a string username. */
+ const char *groupname; /**< CONTEXT: string groupname for post-init
+ * permissions. Like .gid but takes a string groupname. */
+ const char *unix_socket_perms; /**< VHOST: if your vhost is listening
+ * on a unix socket, you can give a "username:groupname" string here
+ * to control the owner:group it's created with. It's always created
+ * with 0660 mode. */
+ const lws_system_ops_t *system_ops;
+ /**< CONTEXT: hook up lws_system_ apis to system-specific
+ * implementations */
+ det_lat_buf_cb_t detailed_latency_cb;
+ /**< CONTEXT: NULL, or callback to receive detailed latency information
+ * collected for each read and write */
+ const char *detailed_latency_filepath;
+ /**< CONTEXT: NULL, or filepath to put latency data into */
+ const lws_retry_bo_t *retry_and_idle_policy;
+ /**< VHOST: optional retry and idle policy to apply to this vhost.
+ * Currently only the idle parts are applied to the connections.
+ */
+ lws_state_notify_link_t * const *register_notifier_list;
+ /**< CONTEXT: NULL, or pointer to an array of notifiers that should
+ * be registered during context creation, so they can see state change
+ * events from very early on. The array should end with a NULL. */
+ uint8_t udp_loss_sim_tx_pc;
+ /**< CONTEXT: percentage of udp writes we could have performed
+ * to instead not do, in order to simulate and test udp retry flow */
+ uint8_t udp_loss_sim_rx_pc;
+ /**< CONTEXT: percentage of udp reads we actually received
+ * to make disappear, in order to simulate and test udp retry flow */
+#if defined(LWS_WITH_SECURE_STREAMS)
+ const char *pss_policies_json; /**< CONTEXT: point to a string
+ * containing a JSON description of the secure streams policies. Set
+ * to NULL if not using Secure Streams. */
+ const struct lws_ss_plugin **pss_plugins; /**< CONTEXT: point to an array
+ * of pointers to plugin structs here, terminated with a NULL ptr.
+ * Set to NULL if not using Secure Streams. */
+ const char *ss_proxy_bind; /**< CONTEXT: NULL, or: ss_proxy_port == 0:
+ * point to a string giving the Unix Domain Socket address to use (start
+ * with @ for abstract namespace), ss_proxy_port nonzero: set the
+ * network interface address (not name, it's ambiguous for ipv4/6) to
+ * bind the tcp connection to the proxy to */
+ const char *ss_proxy_address; /**< CONTEXT: NULL, or if ss_proxy_port
+ * nonzero: the tcp address of the ss proxy to connect to */
+ uint16_t ss_proxy_port; /* 0 = if connecting to ss proxy, do it via a
+ * Unix Domain Socket, "+@proxy.ss.lws" if ss_proxy_bind is NULL else
+ * the socket path given in ss_proxy_bind (start it with a + or +@);
+ * nonzero means connect via a tcp socket to the tcp address in
+ * ss_proxy_bind and the given port */
+#endif
+
+ /* Add new things just above here ---^
+ * This is part of the ABI, don't needlessly break compatibility
+ *
+ * The below is to ensure later library versions with new
+ * members added above will see 0 (default) even if the app
+ * was not built against the newer headers.
+ */
+
+ void *_unused[2]; /**< dummy */
+};
+
+/**
+ * lws_create_context() - Create the websocket handler
+ * \param info: pointer to struct with parameters
+ *
+ * This function creates the listening socket (if serving) and takes care
+ * of all initialization in one step.
+ *
+ * If option LWS_SERVER_OPTION_EXPLICIT_VHOSTS is given, no vhost is
+ * created; you're expected to create your own vhosts afterwards using
+ * lws_create_vhost(). Otherwise a vhost named "default" is also created
+ * using the information in the vhost-related members, for compatibility.
+ *
+ * After initialization, it returns a struct lws_context * that
+ * represents this server. After calling, user code needs to take care
+ * of calling lws_service() with the context pointer to get the
+ * server's sockets serviced. This must be done in the same process
+ * context as the initialization call.
+ *
+ * The protocol callback functions are called for a handful of events
+ * including http requests coming in, websocket connections becoming
+ * established, and data arriving; it's also called periodically to allow
+ * async transmission.
+ *
+ * HTTP requests are sent always to the FIRST protocol in protocol, since
+ * at that time websocket protocol has not been negotiated. Other
+ * protocols after the first one never see any HTTP callback activity.
+ *
+ * The server created is a simple http server by default; part of the
+ * websocket standard is upgrading this http connection to a websocket one.
+ *
+ * This allows the same server to provide files like scripts and favicon /
+ * images or whatever over http and dynamic data over websockets all in
+ * one place; they're all handled in the user callback.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_context *
+lws_create_context(const struct lws_context_creation_info *info);
+
+
+/**
+ * lws_context_destroy() - Destroy the websocket context
+ * \param context: Websocket context
+ *
+ * This function closes any active connections and then frees the
+ * context. After calling this, any further use of the context is
+ * undefined.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_context_destroy(struct lws_context *context);
+
+typedef int (*lws_reload_func)(void);
+
+/**
+ * lws_context_deprecate() - Deprecate the websocket context
+ *
+ * \param context: Websocket context
+ * \param cb: Callback notified when old context listen sockets are closed
+ *
+ * This function is used on an existing context before superceding it
+ * with a new context.
+ *
+ * It closes any listen sockets in the context, so new connections are
+ * not possible.
+ *
+ * And it marks the context to be deleted when the number of active
+ * connections into it falls to zero.
+ *
+ * This is aimed at allowing seamless configuration reloads.
+ *
+ * The callback cb will be called after the listen sockets are actually
+ * closed and may be reopened. In the callback the new context should be
+ * configured and created. (With libuv, socket close happens async after
+ * more loop events).
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_context_deprecate(struct lws_context *context, lws_reload_func cb);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_context_is_deprecated(struct lws_context *context);
+
+/**
+ * lws_set_proxy() - Setups proxy to lws_context.
+ * \param vhost: pointer to struct lws_vhost you want set proxy for
+ * \param proxy: pointer to c string containing proxy in format address:port
+ *
+ * Returns 0 if proxy string was parsed and proxy was setup.
+ * Returns -1 if proxy is NULL or has incorrect format.
+ *
+ * This is only required if your OS does not provide the http_proxy
+ * environment variable (eg, OSX)
+ *
+ * IMPORTANT! You should call this function right after creation of the
+ * lws_context and before call to connect. If you call this
+ * function after connect behavior is undefined.
+ * This function will override proxy settings made on lws_context
+ * creation with genenv() call.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_set_proxy(struct lws_vhost *vhost, const char *proxy);
+
+/**
+ * lws_set_socks() - Setup socks to lws_context.
+ * \param vhost: pointer to struct lws_vhost you want set socks for
+ * \param socks: pointer to c string containing socks in format address:port
+ *
+ * Returns 0 if socks string was parsed and socks was setup.
+ * Returns -1 if socks is NULL or has incorrect format.
+ *
+ * This is only required if your OS does not provide the socks_proxy
+ * environment variable (eg, OSX)
+ *
+ * IMPORTANT! You should call this function right after creation of the
+ * lws_context and before call to connect. If you call this
+ * function after connect behavior is undefined.
+ * This function will override proxy settings made on lws_context
+ * creation with genenv() call.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_set_socks(struct lws_vhost *vhost, const char *socks);
+
+struct lws_vhost;
+
+/**
+ * lws_create_vhost() - Create a vhost (virtual server context)
+ * \param context: pointer to result of lws_create_context()
+ * \param info: pointer to struct with parameters
+ *
+ * This function creates a virtual server (vhost) using the vhost-related
+ * members of the info struct. You can create many vhosts inside one context
+ * if you created the context with the option LWS_SERVER_OPTION_EXPLICIT_VHOSTS
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_vhost *
+lws_create_vhost(struct lws_context *context,
+ const struct lws_context_creation_info *info);
+
+/**
+ * lws_vhost_destroy() - Destroy a vhost (virtual server context)
+ *
+ * \param vh: pointer to result of lws_create_vhost()
+ *
+ * This function destroys a vhost. Normally, if you just want to exit,
+ * then lws_destroy_context() will take care of everything. If you want
+ * to destroy an individual vhost and all connections and allocations, you
+ * can do it with this.
+ *
+ * If the vhost has a listen sockets shared by other vhosts, it will be given
+ * to one of the vhosts sharing it rather than closed.
+ *
+ * The vhost close is staged according to the needs of the event loop, and if
+ * there are multiple service threads. At the point the vhost itself if
+ * about to be freed, if you provided a finalize callback and optional arg at
+ * vhost creation time, it will be called just before the vhost is freed.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_vhost_destroy(struct lws_vhost *vh);
+
+/**
+ * lwsws_get_config_globals() - Parse a JSON server config file
+ * \param info: pointer to struct with parameters
+ * \param d: filepath of the config file
+ * \param config_strings: storage for the config strings extracted from JSON,
+ * the pointer is incremented as strings are stored
+ * \param len: pointer to the remaining length left in config_strings
+ * the value is decremented as strings are stored
+ *
+ * This function prepares a n lws_context_creation_info struct with global
+ * settings from a file d.
+ *
+ * Requires CMake option LWS_WITH_LEJP_CONF to have been enabled
+ */
+LWS_VISIBLE LWS_EXTERN int
+lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d,
+ char **config_strings, int *len);
+
+/**
+ * lwsws_get_config_vhosts() - Create vhosts from a JSON server config file
+ * \param context: pointer to result of lws_create_context()
+ * \param info: pointer to struct with parameters
+ * \param d: filepath of the config file
+ * \param config_strings: storage for the config strings extracted from JSON,
+ * the pointer is incremented as strings are stored
+ * \param len: pointer to the remaining length left in config_strings
+ * the value is decremented as strings are stored
+ *
+ * This function creates vhosts into a context according to the settings in
+ *JSON files found in directory d.
+ *
+ * Requires CMake option LWS_WITH_LEJP_CONF to have been enabled
+ */
+LWS_VISIBLE LWS_EXTERN int
+lwsws_get_config_vhosts(struct lws_context *context,
+ struct lws_context_creation_info *info, const char *d,
+ char **config_strings, int *len);
+
+/**
+ * lws_get_vhost() - return the vhost a wsi belongs to
+ *
+ * \param wsi: which connection
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_vhost *
+lws_get_vhost(struct lws *wsi);
+
+/**
+ * lws_get_vhost_name() - returns the name of a vhost
+ *
+ * \param vhost: which vhost
+ */
+LWS_VISIBLE LWS_EXTERN const char *
+lws_get_vhost_name(struct lws_vhost *vhost);
+
+/**
+ * lws_get_vhost_by_name() - returns the vhost with the requested name, or NULL
+ *
+ * \param context: the lws_context to look in
+ * \param name: vhost name we are looking for
+ *
+ * Returns NULL, or the vhost with the name \p name
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_vhost *
+lws_get_vhost_by_name(struct lws_context *context, const char *name);
+
+/**
+ * lws_get_vhost_port() - returns the port a vhost listens on, or -1
+ *
+ * \param vhost: which vhost
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_get_vhost_port(struct lws_vhost *vhost);
+
+/**
+ * lws_get_vhost_user() - returns the user pointer for the vhost
+ *
+ * \param vhost: which vhost
+ */
+LWS_VISIBLE LWS_EXTERN void *
+lws_get_vhost_user(struct lws_vhost *vhost);
+
+/**
+ * lws_get_vhost_iface() - returns the binding for the vhost listen socket
+ *
+ * \param vhost: which vhost
+ */
+LWS_VISIBLE LWS_EXTERN const char *
+lws_get_vhost_iface(struct lws_vhost *vhost);
+
+/**
+ * lws_json_dump_vhost() - describe vhost state and stats in JSON
+ *
+ * \param vh: the vhost
+ * \param buf: buffer to fill with JSON
+ * \param len: max length of buf
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len);
+
+/**
+ * lws_json_dump_context() - describe context state and stats in JSON
+ *
+ * \param context: the context
+ * \param buf: buffer to fill with JSON
+ * \param len: max length of buf
+ * \param hide_vhosts: nonzero to not provide per-vhost mount etc information
+ *
+ * Generates a JSON description of vhost state into buf
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_json_dump_context(const struct lws_context *context, char *buf, int len,
+ int hide_vhosts);
+
+/**
+ * lws_vhost_user() - get the user data associated with the vhost
+ * \param vhost: Websocket vhost
+ *
+ * This returns the optional user pointer that can be attached to
+ * a vhost when it was created. Lws never dereferences this pointer, it only
+ * sets it when the vhost is created, and returns it using this api.
+ */
+LWS_VISIBLE LWS_EXTERN void *
+lws_vhost_user(struct lws_vhost *vhost);
+
+/**
+ * lws_context_user() - get the user data associated with the context
+ * \param context: Websocket context
+ *
+ * This returns the optional user allocation that can be attached to
+ * the context the sockets live in at context_create time. It's a way
+ * to let all sockets serviced in the same context share data without
+ * using globals statics in the user code.
+ */
+LWS_VISIBLE LWS_EXTERN void *
+lws_context_user(struct lws_context *context);
+
+/*! \defgroup vhost-mounts Vhost mounts and options
+ * \ingroup context-and-vhost-creation
+ *
+ * ##Vhost mounts and options
+ */
+///@{
+/** struct lws_protocol_vhost_options - linked list of per-vhost protocol
+ * name=value options
+ *
+ * This provides a general way to attach a linked-list of name=value pairs,
+ * which can also have an optional child link-list using the options member.
+ */
+struct lws_protocol_vhost_options {
+ const struct lws_protocol_vhost_options *next; /**< linked list */
+ const struct lws_protocol_vhost_options *options; /**< child linked-list of more options for this node */
+ const char *name; /**< name of name=value pair */
+ const char *value; /**< value of name=value pair */
+};
+
+/** enum lws_mount_protocols
+ * This specifies the mount protocol for a mountpoint, whether it is to be
+ * served from a filesystem, or it is a cgi etc.
+ */
+enum lws_mount_protocols {
+ LWSMPRO_HTTP = 0, /**< http reverse proxy */
+ LWSMPRO_HTTPS = 1, /**< https reverse proxy */
+ LWSMPRO_FILE = 2, /**< serve from filesystem directory */
+ LWSMPRO_CGI = 3, /**< pass to CGI to handle */
+ LWSMPRO_REDIR_HTTP = 4, /**< redirect to http:// url */
+ LWSMPRO_REDIR_HTTPS = 5, /**< redirect to https:// url */
+ LWSMPRO_CALLBACK = 6, /**< hand by named protocol's callback */
+};
+
+/** enum lws_authentication_mode
+ * This specifies the authentication mode of the mount. The basic_auth_login_file mount parameter
+ * is ignored unless LWSAUTHM_DEFAULT is set.
+ */
+enum lws_authentication_mode {
+ LWSAUTHM_DEFAULT = 0, /**< default authenticate only if basic_auth_login_file is provided */
+ LWSAUTHM_BASIC_AUTH_CALLBACK = 1 << 28 /**< Basic auth with a custom verifier */
+};
+
+/** The authentication mode is stored in the top 4 bits of lws_http_mount.auth_mask */
+#define AUTH_MODE_MASK 0xF0000000
+
+/** struct lws_http_mount
+ *
+ * arguments for mounting something in a vhost's url namespace
+ */
+struct lws_http_mount {
+ const struct lws_http_mount *mount_next;
+ /**< pointer to next struct lws_http_mount */
+ const char *mountpoint;
+ /**< mountpoint in http pathspace, eg, "/" */
+ const char *origin;
+ /**< path to be mounted, eg, "/var/www/warmcat.com" */
+ const char *def;
+ /**< default target, eg, "index.html" */
+ const char *protocol;
+ /**<"protocol-name" to handle mount */
+
+ const struct lws_protocol_vhost_options *cgienv;
+ /**< optional linked-list of cgi options. These are created
+ * as environment variables for the cgi process
+ */
+ const struct lws_protocol_vhost_options *extra_mimetypes;
+ /**< optional linked-list of mimetype mappings */
+ const struct lws_protocol_vhost_options *interpret;
+ /**< optional linked-list of files to be interpreted */
+
+ int cgi_timeout;
+ /**< seconds cgi is allowed to live, if cgi://mount type */
+ int cache_max_age;
+ /**< max-age for reuse of client cache of files, seconds */
+ unsigned int auth_mask;
+ /**< bits set here must be set for authorized client session */
+
+ unsigned int cache_reusable:1; /**< set if client cache may reuse this */
+ unsigned int cache_revalidate:1; /**< set if client cache should revalidate on use */
+ unsigned int cache_intermediaries:1; /**< set if intermediaries are allowed to cache */
+
+ unsigned char origin_protocol; /**< one of enum lws_mount_protocols */
+ unsigned char mountpoint_len; /**< length of mountpoint string */
+
+ const char *basic_auth_login_file;
+ /**<NULL, or filepath to use to check basic auth logins against. (requires LWSAUTHM_DEFAULT) */
+
+ /* Add new things just above here ---^
+ * This is part of the ABI, don't needlessly break compatibility
+ *
+ * The below is to ensure later library versions with new
+ * members added above will see 0 (default) even if the app
+ * was not built against the newer headers.
+ */
+
+ void *_unused[2]; /**< dummy */
+};
+
+///@}
+///@}
--- /dev/null
+ /*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * must be included manually as
+ *
+ * #include <libwebsockets/lws-dbus.h>
+ *
+ * if dbus apis needed
+ */
+
+#if !defined(__LWS_DBUS_H__)
+#define __LWS_DBUS_H__
+
+#include <dbus/dbus.h>
+
+/* helper type to simplify implementing methods as individual functions */
+typedef DBusHandlerResult (*lws_dbus_message_handler)(DBusConnection *conn,
+ DBusMessage *message, DBusMessage **reply, void *d);
+
+struct lws_dbus_ctx;
+typedef void (*lws_dbus_closing_t)(struct lws_dbus_ctx *ctx);
+
+struct lws_dbus_ctx {
+ struct lws_dll2_owner owner; /* dbusserver ctx: HEAD of accepted list */
+ struct lws_dll2 next; /* dbusserver ctx: HEAD of accepted list */
+ struct lws_vhost *vh; /* the vhost we logically bind to in lws */
+ int tsi; /* the lws thread service index (0 if only one service
+ thread as is the default */
+ DBusConnection *conn;
+ DBusServer *dbs;
+ DBusWatch *w[4];
+ DBusPendingCall *pc;
+
+ char hup;
+ char timeouts;
+
+ /* cb_closing callback will be called after the connection and this
+ * related ctx struct have effectively gone out of scope.
+ *
+ * The callback should close and clean up the connection and free the
+ * ctx.
+ */
+ lws_dbus_closing_t cb_closing;
+};
+
+/**
+ * lws_dbus_connection_setup() - bind dbus connection object to lws event loop
+ *
+ * \param ctx: additional information about the connection
+ * \param conn: the DBusConnection object to bind
+ *
+ * This configures a DBusConnection object to use lws for watchers and timeout
+ * operations.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_dbus_connection_setup(struct lws_dbus_ctx *ctx, DBusConnection *conn,
+ lws_dbus_closing_t cb_closing);
+
+/**
+ * lws_dbus_server_listen() - bind dbus connection object to lws event loop
+ *
+ * \param ctx: additional information about the connection
+ * \param ads: the DBUS address to listen on, eg, "unix:abstract=mysocket"
+ * \param err: a DBusError object to take any extra error information
+ * \param new_conn: a callback function to prepare new accepted connections
+ *
+ * This creates a DBusServer and binds it to the lws event loop, and your
+ * callback to accept new connections.
+ */
+LWS_VISIBLE LWS_EXTERN DBusServer *
+lws_dbus_server_listen(struct lws_dbus_ctx *ctx, const char *ads,
+ DBusError *err, DBusNewConnectionFunction new_conn);
+
+#endif
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * included from libwebsockets.h
+ */
+
+enum {
+
+ /* types of latency, all nonblocking except name resolution */
+
+ LDLT_READ, /* time taken to read LAT_DUR_PROXY_RX_TO_CLIENT_WRITE */
+ LDLT_WRITE,
+ LDLT_NAME_RESOLUTION, /* BLOCKING: LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE */
+ LDLT_CONNECTION, /* conn duration: LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE */
+ LDLT_TLS_NEG_CLIENT, /* tls conn duration: LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE */
+ LDLT_TLS_NEG_SERVER, /* tls conn duration: LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE */
+
+ LDLT_USER,
+
+ /* interval / duration elements in latencies array */
+
+ LAT_DUR_PROXY_CLIENT_REQ_TO_WRITE = 0,
+ /* us the client spent waiting to write to proxy */
+ LAT_DUR_PROXY_CLIENT_WRITE_TO_PROXY_RX,
+ /* us the packet took to be received by proxy */
+ LAT_DUR_PROXY_PROXY_REQ_TO_WRITE,
+ /* us the proxy has to wait before it could write */
+ LAT_DUR_PROXY_RX_TO_ONWARD_TX,
+ /* us the proxy spent waiting to write to destination, or
+ * if nonproxied, then time between write request and write */
+
+ LAT_DUR_USERCB, /* us duration of user callback */
+
+ LAT_DUR_STEPS /* last */
+};
+
+typedef struct lws_detlat {
+ lws_usec_t earliest_write_req;
+ lws_usec_t earliest_write_req_pre_write;
+ /**< use this for interval comparison */
+ const char *aux; /* name for name resolution timing */
+ int type;
+ uint32_t latencies[LAT_DUR_STEPS];
+ size_t req_size;
+ size_t acc_size;
+} lws_detlat_t;
+
+typedef int (*det_lat_buf_cb_t)(struct lws_context *context,
+ const lws_detlat_t *d);
+
+/**
+ * lws_det_lat_cb() - inject your own latency records
+ *
+ * \param context: the lws_context
+ * \param d: the lws_detlat_t you have prepared
+ *
+ * For proxying or similar cases where latency information is available from
+ * user code rather than lws itself, you can generate your own latency callback
+ * events with your own lws_detlat_t.
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_det_lat_cb(struct lws_context *context, lws_detlat_t *d);
+
+/*
+ * detailed_latency_plot_cb() - canned save to file in plottable format cb
+ *
+ * \p context: the lws_context
+ * \p d: the detailed latency event information
+ *
+ * This canned callback makes it easy to export the detailed latency information
+ * to a file. Just set the context creation members like this
+ *
+ * #if defined(LWS_WITH_DETAILED_LATENCY)
+ * info.detailed_latency_cb = lws_det_lat_plot_cb;
+ * info.detailed_latency_filepath = "/tmp/lws-latency-results";
+ * #endif
+ *
+ * and you will get a file containing information like this
+ *
+ * 718823864615 N 10589 0 0 10589 0 0 0
+ * 718823880837 C 16173 0 0 16173 0 0 0
+ * 718823913063 T 32212 0 0 32212 0 0 0
+ * 718823931835 r 0 0 0 0 232 30 256
+ * 718823948757 r 0 0 0 0 40 30 256
+ * 718823948799 r 0 0 0 0 83 30 256
+ * 718823965602 r 0 0 0 0 27 30 256
+ * 718823965617 r 0 0 0 0 43 30 256
+ * 718823965998 r 0 0 0 0 12 28 256
+ * 718823983887 r 0 0 0 0 74 3 4096
+ * 718823986411 w 16 87 7 110 9 80 80
+ * 718824006358 w 8 68 6 82 6 80 80
+ *
+ * which is easy to grep and pass to gnuplot.
+ *
+ * The columns are
+ *
+ * - unix time in us
+ * - N = Name resolution, C = TCP Connection, T = TLS negotiation server,
+ * t = TLS negotiation client, r = Read, w = Write
+ * - us duration, for w time client spent waiting to write
+ * - us duration, for w time data spent in transit to proxy
+ * - us duration, for w time proxy waited to send data
+ * - as a convenience, sum of last 3 columns above
+ * - us duration, time spent in callback
+ * - last 2 are actual / requested size in bytes
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_det_lat_plot_cb(struct lws_context *context, const lws_detlat_t *d);
+
+/**
+ * lws_det_lat_active() - indicates if latencies are being measured
+ *
+ * \context: lws_context
+ *
+ * Returns 0 if latency measurement has not been set up (the callback is NULL).
+ * Otherwise returns 1
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_det_lat_active(struct lws_context *context);
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*! \defgroup diskcache LWS disk cache
+ * ## Disk cache API
+ *
+ * Lws provides helper apis useful if you need a disk cache containing hashed
+ * files and need to delete files from it on an LRU basis to keep it below some
+ * size limit.
+ *
+ * The API `lws_diskcache_prepare()` deals with creating the cache dir and
+ * 256 subdirs, which are used according to the first two chars of the hex
+ * hash of the cache file.
+ *
+ * `lws_diskcache_create()` and `lws_diskcache_destroy()` allocate and free
+ * an opaque struct that represents the disk cache.
+ *
+ * `lws_diskcache_trim()` should be called at eg, 1s intervals to perform the
+ * cache dir monitoring and LRU autodelete in the background lazily. It can
+ * be done in its own thread or on a timer... it monitors the directories in a
+ * stateful way that stats one or more file in the cache per call, and keeps
+ * a list of the oldest files as it goes. When it completes a scan, if the
+ * aggregate size is over the limit, it will delete oldest files first to try
+ * to keep it under the limit.
+ *
+ * The cache size monitoring is extremely efficient in time and memory even when
+ * the cache directory becomes huge.
+ *
+ * `lws_diskcache_query()` is used to determine if the file already exists in
+ * the cache, or if it must be created. If it must be created, then the file
+ * is opened using a temp name that must be converted to a findable name with
+ * `lws_diskcache_finalize_name()` when the generation of the file contents are
+ * complete. Aborted cached files that did not complete generation will be
+ * flushed by the LRU eventually. If the file already exists, it is 'touched'
+ * to make it new again and the fd returned.
+ *
+ */
+///@{
+
+struct lws_diskcache_scan;
+
+/**
+ * lws_diskcache_create() - creates an opaque struct representing the disk cache
+ *
+ * \param cache_dir_base: The cache dir path, eg `/var/cache/mycache`
+ * \param cache_size_limit: maximum size on disk the cache is allowed to use
+ *
+ * This returns an opaque `struct lws_diskcache_scan *` which represents the
+ * disk cache, the trim scanning state and so on. You should use
+ * `lws_diskcache_destroy()` to free it to destroy it.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_diskcache_scan *
+lws_diskcache_create(const char *cache_dir_base, uint64_t cache_size_limit);
+
+/**
+ * lws_diskcache_destroy() - destroys the pointer returned by ...create()
+ *
+ * \param lds: pointer to the pointer returned by lws_diskcache_create()
+ *
+ * Frees *lds and any allocations it did, and then sets *lds to NULL and
+ * returns.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_diskcache_destroy(struct lws_diskcache_scan **lds);
+
+/**
+ * lws_diskcache_prepare() - ensures the cache dir structure exists on disk
+ *
+ * \param cache_base_dir: The cache dir path, eg `/var/cache/mycache`
+ * \param mode: octal dir mode to enforce, like 0700
+ * \param uid: uid the cache dir should belong to
+ *
+ * This should be called while your app is still privileged. It will create
+ * the cache directory structure on disk as necessary, enforce the given access
+ * mode on it and set the given uid as the owner. It won't make any trouble
+ * if the cache already exists.
+ *
+ * Typically the mode is 0700 and the owner is the user that your application
+ * will transition to use when it drops root privileges.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_diskcache_prepare(const char *cache_base_dir, int mode, int uid);
+
+#define LWS_DISKCACHE_QUERY_NO_CACHE 0
+#define LWS_DISKCACHE_QUERY_EXISTS 1
+#define LWS_DISKCACHE_QUERY_CREATING 2
+#define LWS_DISKCACHE_QUERY_ONGOING 3 /* something else is creating it */
+
+/**
+ * lws_diskcache_query() - ensures the cache dir structure exists on disk
+ *
+ * \param lds: The opaque struct representing the disk cache
+ * \param is_bot: nonzero means the request is from a bot. Don't create new cache contents if so.
+ * \param hash_hex: hex string representation of the cache object hash
+ * \param _fd: pointer to the fd to be set
+ * \param cache: destination string to take the cache filepath
+ * \param cache_len: length of the buffer at `cache`
+ * \param extant_cache_len: pointer to a size_t to take any extant cached file size
+ *
+ * This function is called when you want to find if the hashed name already
+ * exists in the cache. The possibilities for the return value are
+ *
+ * - LWS_DISKCACHE_QUERY_NO_CACHE: It's not in the cache and you can't create
+ * it in the cache for whatever reason.
+ * - LWS_DISKCACHE_QUERY_EXISTS: It exists in the cache. It's open RDONLY and
+ * *_fd has been set to the file descriptor. *extant_cache_len has been set
+ * to the size of the cached file in bytes. cache has been set to the
+ * full filepath of the cached file. Closing _fd is your responsibility.
+ * - LWS_DISKCACHE_QUERY_CREATING: It didn't exist, but a temp file has been
+ * created in the cache and *_fd set to a file descriptor opened on it RDWR.
+ * You should create the contents, and call `lws_diskcache_finalize_name()`
+ * when it is done. Closing _fd is your responsibility.
+ * - LWS_DISKCACHE_QUERY_ONGOING: not returned by this api, but you may find it
+ * desirable to make a wrapper function which can handle another asynchronous
+ * process that is already creating the cached file. This can be used to
+ * indicate that situation externally... how to determine the same thing is
+ * already being generated is out of scope of this api.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_diskcache_query(struct lws_diskcache_scan *lds, int is_bot,
+ const char *hash_hex, int *_fd, char *cache, int cache_len,
+ size_t *extant_cache_len);
+
+/**
+ * lws_diskcache_query() - ensures the cache dir structure exists on disk
+ *
+ * \param cache: The cache file temp name returned with LWS_DISKCACHE_QUERY_CREATING
+ *
+ * This renames the cache file you are creating to its final name. It should
+ * be called on the temp name returned by `lws_diskcache_query()` if it gave a
+ * LWS_DISKCACHE_QUERY_CREATING return, after you have filled the cache file and
+ * closed it.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_diskcache_finalize_name(char *cache);
+
+/**
+ * lws_diskcache_trim() - performs one or more file checks in the cache for size management
+ *
+ * \param lds: The opaque object representing the cache
+ *
+ * This should be called periodically to statefully walk the cache on disk
+ * collecting the oldest files. When it has visited every file, if the cache
+ * is oversize it will delete the oldest files until it's back under size again.
+ *
+ * Each time it's called, it will look at one or more dir in the cache. If
+ * called when the cache is oversize, it increases the amount of work done each
+ * call until it is reduced again. Typically it will take 256 calls before it
+ * deletes anything, so if called once per second, it will delete files once
+ * every 4 minutes. Each call is very inexpensive both in memory and time.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_diskcache_trim(struct lws_diskcache_scan *lds);
+
+
+/**
+ * lws_diskcache_secs_to_idle() - see how long to idle before calling trim
+ *
+ * \param lds: The opaque object representing the cache
+ *
+ * If the cache is undersize, there's no need to monitor it immediately. This
+ * suggests how long to "sleep" before calling `lws_diskcache_trim()` again.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_diskcache_secs_to_idle(struct lws_diskcache_scan *lds);
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/** \defgroup ll linked-lists
+* ##Linked list apis
+*
+* simple single and doubly-linked lists
+*/
+///@{
+
+/**
+ * lws_start_foreach_ll(): linkedlist iterator helper start
+ *
+ * \param type: type of iteration, eg, struct xyz *
+ * \param it: iterator var name to create
+ * \param start: start of list
+ *
+ * This helper creates an iterator and starts a while (it) {
+ * loop. The iterator runs through the linked list starting at start and
+ * ends when it gets a NULL.
+ * The while loop should be terminated using lws_start_foreach_ll().
+ */
+#define lws_start_foreach_ll(type, it, start)\
+{ \
+ type it = start; \
+ while (it) {
+
+/**
+ * lws_end_foreach_ll(): linkedlist iterator helper end
+ *
+ * \param it: same iterator var name given when starting
+ * \param nxt: member name in the iterator pointing to next list element
+ *
+ * This helper is the partner for lws_start_foreach_ll() that ends the
+ * while loop.
+ */
+
+#define lws_end_foreach_ll(it, nxt) \
+ it = it->nxt; \
+ } \
+}
+
+/**
+ * lws_start_foreach_ll_safe(): linkedlist iterator helper start safe against delete
+ *
+ * \param type: type of iteration, eg, struct xyz *
+ * \param it: iterator var name to create
+ * \param start: start of list
+ * \param nxt: member name in the iterator pointing to next list element
+ *
+ * This helper creates an iterator and starts a while (it) {
+ * loop. The iterator runs through the linked list starting at start and
+ * ends when it gets a NULL.
+ * The while loop should be terminated using lws_end_foreach_ll_safe().
+ * Performs storage of next increment for situations where iterator can become invalidated
+ * during iteration.
+ */
+#define lws_start_foreach_ll_safe(type, it, start, nxt)\
+{ \
+ type it = start; \
+ while (it) { \
+ type next_##it = it->nxt;
+
+/**
+ * lws_end_foreach_ll_safe(): linkedlist iterator helper end (pre increment storage)
+ *
+ * \param it: same iterator var name given when starting
+ *
+ * This helper is the partner for lws_start_foreach_ll_safe() that ends the
+ * while loop. It uses the precreated next_ variable already stored during
+ * start.
+ */
+
+#define lws_end_foreach_ll_safe(it) \
+ it = next_##it; \
+ } \
+}
+
+/**
+ * lws_start_foreach_llp(): linkedlist pointer iterator helper start
+ *
+ * \param type: type of iteration, eg, struct xyz **
+ * \param it: iterator var name to create
+ * \param start: start of list
+ *
+ * This helper creates an iterator and starts a while (it) {
+ * loop. The iterator runs through the linked list starting at the
+ * address of start and ends when it gets a NULL.
+ * The while loop should be terminated using lws_start_foreach_llp().
+ *
+ * This helper variant iterates using a pointer to the previous linked-list
+ * element. That allows you to easily delete list members by rewriting the
+ * previous pointer to the element's next pointer.
+ */
+#define lws_start_foreach_llp(type, it, start)\
+{ \
+ type it = &(start); \
+ while (*(it)) {
+
+#define lws_start_foreach_llp_safe(type, it, start, nxt)\
+{ \
+ type it = &(start); \
+ type next; \
+ while (*(it)) { \
+ next = &((*(it))->nxt); \
+
+/**
+ * lws_end_foreach_llp(): linkedlist pointer iterator helper end
+ *
+ * \param it: same iterator var name given when starting
+ * \param nxt: member name in the iterator pointing to next list element
+ *
+ * This helper is the partner for lws_start_foreach_llp() that ends the
+ * while loop.
+ */
+
+#define lws_end_foreach_llp(it, nxt) \
+ it = &(*(it))->nxt; \
+ } \
+}
+
+#define lws_end_foreach_llp_safe(it) \
+ it = next; \
+ } \
+}
+
+#define lws_ll_fwd_insert(\
+ ___new_object, /* pointer to new object */ \
+ ___m_list, /* member for next list object ptr */ \
+ ___list_head /* list head */ \
+ ) {\
+ ___new_object->___m_list = ___list_head; \
+ ___list_head = ___new_object; \
+ }
+
+#define lws_ll_fwd_remove(\
+ ___type, /* type of listed object */ \
+ ___m_list, /* member for next list object ptr */ \
+ ___target, /* object to remove from list */ \
+ ___list_head /* list head */ \
+ ) { \
+ lws_start_foreach_llp(___type **, ___ppss, ___list_head) { \
+ if (*___ppss == ___target) { \
+ *___ppss = ___target->___m_list; \
+ break; \
+ } \
+ } lws_end_foreach_llp(___ppss, ___m_list); \
+ }
+
+
+/*
+ * doubly linked-list
+ */
+
+#if defined (LWS_WITH_DEPRECATED_LWS_DLL)
+
+/*
+ * This is going away in v4.1. You can set the cmake option above to keep it
+ * around temporarily. Migrate your stuff to the more capable and robust
+ * lws_dll2 below
+ */
+
+struct lws_dll {
+ struct lws_dll *prev;
+ struct lws_dll *next;
+};
+
+/*
+ * these all point to the composed list objects... you have to use the
+ * lws_container_of() helper to recover the start of the containing struct
+ */
+
+#define lws_dll_add_front lws_dll_add_head
+
+LWS_VISIBLE LWS_EXTERN void
+lws_dll_add_head(struct lws_dll *d, struct lws_dll *phead);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_dll_add_tail(struct lws_dll *d, struct lws_dll *phead);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_dll_insert(struct lws_dll *d, struct lws_dll *target,
+ struct lws_dll *phead, int before);
+
+static LWS_INLINE struct lws_dll *
+lws_dll_get_head(struct lws_dll *phead) { return phead->next; }
+
+static LWS_INLINE struct lws_dll *
+lws_dll_get_tail(struct lws_dll *phead) { return phead->prev; }
+
+/*
+ * caution, this doesn't track the tail in the head struct. Use
+ * lws_dll_remove_track_tail() instead of this if you want tail tracking. Using
+ * this means you can't use lws_dll_add_tail() amd
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_dll_remove(struct lws_dll *d) LWS_WARN_DEPRECATED;
+
+LWS_VISIBLE LWS_EXTERN void
+lws_dll_remove_track_tail(struct lws_dll *d, struct lws_dll *phead);
+
+/* another way to do lws_start_foreach_dll_safe() on a list via a cb */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_dll_foreach_safe(struct lws_dll *phead, void *user,
+ int (*cb)(struct lws_dll *d, void *user));
+
+#define lws_dll_is_detached(___dll, __head) \
+ (!(___dll)->prev && !(___dll)->next && (__head)->prev != (___dll))
+
+#endif
+
+/*
+ * lws_dll2_owner / lws_dll2 : more capable version of lws_dll. Differences:
+ *
+ * - there's an explicit lws_dll2_owner struct which holds head, tail and
+ * count of members.
+ *
+ * - list members all hold a pointer to their owner. So user code does not
+ * have to track anything about exactly what lws_dll2_owner list the object
+ * is a member of.
+ *
+ * - you can use lws_dll unless you want the member count or the ability to
+ * not track exactly which list it's on.
+ *
+ * - layout is compatible with lws_dll (but lws_dll apis will not update the
+ * new stuff)
+ */
+
+
+struct lws_dll2;
+struct lws_dll2_owner;
+
+typedef struct lws_dll2 {
+ struct lws_dll2 *prev;
+ struct lws_dll2 *next;
+ struct lws_dll2_owner *owner;
+} lws_dll2_t;
+
+typedef struct lws_dll2_owner {
+ struct lws_dll2 *tail;
+ struct lws_dll2 *head;
+
+ uint32_t count;
+} lws_dll2_owner_t;
+
+static LWS_INLINE int
+lws_dll2_is_detached(const struct lws_dll2 *d) { return !d->owner; }
+
+static LWS_INLINE const struct lws_dll2_owner *
+lws_dll2_owner(const struct lws_dll2 *d) { return d->owner; }
+
+static LWS_INLINE struct lws_dll2 *
+lws_dll2_get_head(struct lws_dll2_owner *owner) { return owner->head; }
+
+static LWS_INLINE struct lws_dll2 *
+lws_dll2_get_tail(struct lws_dll2_owner *owner) { return owner->tail; }
+
+LWS_VISIBLE LWS_EXTERN void
+lws_dll2_add_head(struct lws_dll2 *d, struct lws_dll2_owner *owner);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_dll2_add_tail(struct lws_dll2 *d, struct lws_dll2_owner *owner);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_dll2_remove(struct lws_dll2 *d);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_dll2_foreach_safe(struct lws_dll2_owner *owner, void *user,
+ int (*cb)(struct lws_dll2 *d, void *user));
+
+LWS_VISIBLE LWS_EXTERN void
+lws_dll2_clear(struct lws_dll2 *d);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_dll2_owner_clear(struct lws_dll2_owner *d);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_dll2_add_before(struct lws_dll2 *d, struct lws_dll2 *after);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_dll2_add_sorted(lws_dll2_t *d, lws_dll2_owner_t *own,
+ int (*compare)(const lws_dll2_t *d, const lws_dll2_t *i));
+
+#if defined(_DEBUG)
+void
+lws_dll2_describe(struct lws_dll2_owner *owner, const char *desc);
+#else
+#define lws_dll2_describe(x, y)
+#endif
+
+/*
+ * these are safe against the current container object getting deleted,
+ * since the hold his next in a temp and go to that next. ___tmp is
+ * the temp.
+ */
+
+#define lws_start_foreach_dll_safe(___type, ___it, ___tmp, ___start) \
+{ \
+ ___type ___it = ___start; \
+ while (___it) { \
+ ___type ___tmp = (___it)->next;
+
+#define lws_end_foreach_dll_safe(___it, ___tmp) \
+ ___it = ___tmp; \
+ } \
+}
+
+#define lws_start_foreach_dll(___type, ___it, ___start) \
+{ \
+ ___type ___it = ___start; \
+ while (___it) {
+
+#define lws_end_foreach_dll(___it) \
+ ___it = (___it)->next; \
+ } \
+}
+
+///@}
+
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*
+ * lws_dsh (Disordered Shared Heap) is an opaque abstraction supporting a single
+ * linear buffer (overallocated at end of the lws_dsh_t) which may contain
+ * multiple kinds of packets that are retired out of order, and tracked by kind.
+ *
+ * Each kind of packet has an lws_dll2 list of its kind of packets and acts as
+ * a FIFO; packets of a particular type are always retired in order. But there
+ * is no requirement about the order types are retired matching the original
+ * order they arrived.
+ *
+ * Gaps are tracked as just another kind of "packet" list.
+ *
+ * "allocations" (including gaps) are prepended by an lws_dsh_object_t.
+ *
+ * dsh may themselves be on an lws_dll2_owner list, and under memory pressure
+ * allocate into other buffers on the list.
+ *
+ * All management structures exist inside the allocated buffer.
+ */
+
+/**
+ * lws_dsh_create() - Allocate a DSH buffer
+ *
+ * \param owner: the owning list this dsh belongs on, or NULL if standalone
+ * \param buffer_size: the allocation in bytes
+ * \param count_kinds: how many separately-tracked fifos use the buffer
+ *
+ * This makes a single heap allocation that includes internal tracking objects
+ * in the buffer. Sub-allocated objects are bound to a "kind" index and
+ * managed via a FIFO for each kind.
+ *
+ * Every "kind" of allocation shares the same buffer space.
+ *
+ * Multiple buffers may be bound together in an lws_dll2 list, and if an
+ * allocation cannot be satisfied by the local buffer, space can be borrowed
+ * from other dsh in the same list (the local dsh FIFO tracks these "foreign"
+ * allocations as if they were local).
+ *
+ * Returns an opaque pointer to the dsh, or NULL if allocation failed.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_dsh *
+lws_dsh_create(lws_dll2_owner_t *owner, size_t buffer_size, int count_kinds);
+
+/**
+ * lws_dsh_destroy() - Destroy a DSH buffer
+ *
+ * \param pdsh: pointer to the dsh pointer
+ *
+ * Deallocates the DSH and sets *pdsh to NULL.
+ *
+ * Before destruction, any foreign buffer usage on the part of this dsh are
+ * individually freed. All dsh on the same list are walked and checked if they
+ * have their own foreign allocations on the dsh buffer being destroyed. If so,
+ * it attempts to migrate the allocation to a dsh that is not currently being
+ * destroyed. If all else fails (basically the buffer memory is being shrunk)
+ * unmigratable objects are cleanly destroyed.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_dsh_destroy(struct lws_dsh **pdsh);
+
+/**
+ * lws_dsh_alloc_tail() - make a suballocation inside a dsh
+ *
+ * \param dsh: the dsh tracking the allocation
+ * \param kind: the kind of allocation
+ * \param src1: the first source data to copy
+ * \param size1: the size of the first source data
+ * \param src2: the second source data to copy (after the first), or NULL
+ * \param size2: the size of the second source data
+ *
+ * Allocates size1 + size2 bytes in a dsh (it prefers the given dsh but will
+ * borrow space from other dsh on the same list if necessary) and copies size1
+ * bytes into it from src1, followed by size2 bytes from src2 if src2 isn't
+ * NULL. The actual suballocation is a bit larger because of alignment and a
+ * prepended management header.
+ *
+ * The suballocation is added to the kind-specific FIFO at the tail.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_dsh_alloc_tail(struct lws_dsh *dsh, int kind, const void *src1,
+ size_t size1, const void *src2, size_t size2);
+
+/**
+ * lws_dsh_free() - free a suballocation from the dsh
+ *
+ * \param obj: a pointer to a void * that pointed to the allocated payload
+ *
+ * This returns the space used by \p obj in the dsh buffer to the free list
+ * of the dsh the allocation came from.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_dsh_free(void **obj);
+
+/**
+ * lws_dsh_get_head() - get the head allocation inside the dsh
+ *
+ * \param dsh: the dsh tracking the allocation
+ * \param kind: the kind of allocation
+ * \param obj: pointer to a void * to be set to the payload
+ * \param size: set to the size of the allocation
+ *
+ * This gets the "next" object in the kind FIFO for the dsh, and returns 0 if
+ * any. If none, returns nonzero.
+ *
+ * This is nondestructive of the fifo or the payload. Use lws_dsh_free on
+ * obj to remove the entry from the kind fifo and return the payload to the
+ * free list.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_dsh_get_head(struct lws_dsh *dsh, int kind, void **obj, size_t *size);
+
+/**
+ * lws_dsh_describe() - DEBUG BUILDS ONLY dump the dsh to the logs
+ *
+ * \param dsh: the dsh to dump
+ * \param desc: text that appears at the top of the dump
+ *
+ * Useful information for debugging lws_dsh
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_dsh_describe(struct lws_dsh *dsh, const char *desc);
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * This is only included from libwebsockets.h if LWS_WITH_ESP32
+ */
+
+/* ESP32 helper declarations */
+
+#include <mdns.h>
+#include <esp_partition.h>
+
+#define LWS_PLUGIN_STATIC
+#define LWS_MAGIC_REBOOT_TYPE_ADS 0x50001ffc
+#define LWS_MAGIC_REBOOT_TYPE_REQ_FACTORY 0xb00bcafe
+#define LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY 0xfaceb00b
+#define LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON 0xf0cedfac
+#define LWS_MAGIC_REBOOT_TYPE_REQ_FACTORY_ERASE_OTA 0xfac0eeee
+
+/* user code provides these */
+
+extern void
+lws_esp32_identify_physical_device(void);
+
+/* lws-plat-esp32 provides these */
+
+typedef void (*lws_cb_scan_done)(uint16_t count, wifi_ap_record_t *recs, void *arg);
+
+enum genled_state {
+ LWSESP32_GENLED__INIT,
+ LWSESP32_GENLED__LOST_NETWORK,
+ LWSESP32_GENLED__NO_NETWORK,
+ LWSESP32_GENLED__CONN_AP,
+ LWSESP32_GENLED__GOT_IP,
+ LWSESP32_GENLED__OK,
+};
+
+struct lws_group_member {
+ struct lws_group_member *next;
+ uint64_t last_seen;
+ char model[16];
+ char role[16];
+ char host[32];
+ char mac[20];
+ int width, height;
+ struct ip4_addr addr;
+ struct ip6_addr addrv6;
+ uint8_t flags;
+};
+
+#define LWS_SYSTEM_GROUP_MEMBER_ADD 1
+#define LWS_SYSTEM_GROUP_MEMBER_CHANGE 2
+#define LWS_SYSTEM_GROUP_MEMBER_REMOVE 3
+
+#define LWS_GROUP_FLAG_SELF 1
+
+struct lws_esp32 {
+ char sta_ip[16];
+ char sta_mask[16];
+ char sta_gw[16];
+ char serial[16];
+ char opts[16];
+ char model[16];
+ char group[16];
+ char role[16];
+ char ssid[4][64];
+ char password[4][64];
+ char active_ssid[64];
+ char access_pw[16];
+ char hostname[32];
+ char mac[20];
+ char le_dns[64];
+ char le_email[64];
+ char region;
+ char inet;
+ char conn_ap;
+
+ enum genled_state genled;
+ uint64_t genled_t;
+
+ lws_cb_scan_done scan_consumer;
+ void *scan_consumer_arg;
+ struct lws_group_member *first;
+ int extant_group_members;
+
+ char acme;
+ char upload;
+
+ volatile char button_is_down;
+};
+
+struct lws_esp32_image {
+ uint32_t romfs;
+ uint32_t romfs_len;
+ uint32_t json;
+ uint32_t json_len;
+};
+
+extern struct lws_esp32 lws_esp32;
+struct lws_vhost;
+
+extern esp_err_t
+lws_esp32_event_passthru(void *ctx, system_event_t *event);
+extern void
+lws_esp32_wlan_config(void);
+extern void
+lws_esp32_wlan_start_ap(void);
+extern void
+lws_esp32_wlan_start_station(void);
+struct lws_context_creation_info;
+extern void
+lws_esp32_set_creation_defaults(struct lws_context_creation_info *info);
+extern struct lws_context *
+lws_esp32_init(struct lws_context_creation_info *, struct lws_vhost **pvh);
+extern int
+lws_esp32_wlan_nvs_get(int retry);
+extern esp_err_t
+lws_nvs_set_str(nvs_handle handle, const char* key, const char* value);
+extern void
+lws_esp32_restart_guided(uint32_t type);
+extern const esp_partition_t *
+lws_esp_ota_get_boot_partition(void);
+extern int
+lws_esp32_get_image_info(const esp_partition_t *part, struct lws_esp32_image *i, char *json, int json_len);
+extern int
+lws_esp32_leds_network_indication(void);
+
+extern uint32_t lws_esp32_get_reboot_type(void);
+extern uint16_t lws_esp32_sine_interp(int n);
+
+/* required in external code by esp32 plat (may just return if no leds) */
+extern void lws_esp32_leds_timer_cb(TimerHandle_t th);
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * This is included from libwebsockets.h if LWS_PLAT_FREERTOS
+ */
+
+typedef int lws_sockfd_type;
+typedef int lws_filefd_type;
+
+#if defined(LWS_AMAZON_RTOS)
+#include <FreeRTOS.h>
+#include <event_groups.h>
+#include <string.h>
+#include "timers.h"
+#include <lwip/sockets.h>
+
+/*
+ * Later lwip (at least 2.1.12) already defines these in its own headers
+ * protected by the same test as used here... if POLLIN / POLLOUT already exist
+ * then assume no need to declare those and struct pollfd.
+ *
+ * Older lwip needs these declarations done here.
+ */
+
+#if !defined(POLLIN) && !defined(POLLOUT)
+
+struct pollfd {
+ lws_sockfd_type fd; /**< fd related to */
+ short events; /**< which POLL... events to respond to */
+ short revents; /**< which POLL... events occurred */
+};
+#define POLLIN 0x0001
+#define POLLPRI 0x0002
+#define POLLOUT 0x0004
+#define POLLERR 0x0008
+#define POLLHUP 0x0010
+#define POLLNVAL 0x0020
+
+#endif
+
+#else /* LWS_AMAZON_RTOS */
+#include <freertos/FreeRTOS.h>
+#include <freertos/event_groups.h>
+#include <string.h>
+#include "esp_wifi.h"
+#include "esp_system.h"
+#include "esp_event.h"
+#include "esp_event_loop.h"
+#include "nvs.h"
+#include "driver/gpio.h"
+#include "esp_spi_flash.h"
+#include "freertos/timers.h"
+#endif /* LWS_AMAZON_RTOS */
+
+#if !defined(CONFIG_FREERTOS_HZ)
+#define CONFIG_FREERTOS_HZ 100
+#endif
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/** \defgroup search Search
+ *
+ * ##Full-text search
+ *
+ * Lws provides superfast indexing and fulltext searching from index files on
+ * storage.
+ */
+///@{
+
+struct lws_fts;
+struct lws_fts_file;
+
+/*
+ * Queries produce their results in an lwsac, using these public API types.
+ * The first thing in the lwsac is always a struct lws_fts_result (see below)
+ * containing heads for linked-lists of the other result types.
+ */
+
+/* one filepath's results */
+
+struct lws_fts_result_filepath {
+ struct lws_fts_result_filepath *next;
+ int matches; /* logical number of matches */
+ int matches_length; /* bytes in length table (may be zero) */
+ int lines_in_file;
+ int filepath_length;
+
+ /* - uint32_t line table follows (first for alignment) */
+ /* - filepath (of filepath_length) follows */
+};
+
+/* autocomplete result */
+
+struct lws_fts_result_autocomplete {
+ struct lws_fts_result_autocomplete *next;
+ int instances;
+ int agg_instances;
+ int ac_length;
+ char elided; /* children skipped in interest of antecedent children */
+ char has_children;
+
+ /* - autocomplete suggestion (of length ac_length) follows */
+};
+
+/*
+ * The results lwsac always starts with this. If no results and / or no
+ * autocomplete the members may be NULL. This implies the symbol nor any
+ * suffix on it exists in the trie file.
+ */
+struct lws_fts_result {
+ struct lws_fts_result_filepath *filepath_head;
+ struct lws_fts_result_autocomplete *autocomplete_head;
+ int duration_ms;
+ int effective_flags; /* the search flags that were used */
+};
+
+/*
+ * index creation functions
+ */
+
+/**
+ * lws_fts_create() - Create a new index file
+ *
+ * \param fd: The fd opened for write
+ *
+ * Inits a new index file, returning a struct lws_fts to represent it
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_fts *
+lws_fts_create(int fd);
+
+/**
+ * lws_fts_destroy() - Finalize a new index file / destroy the trie lwsac
+ *
+ * \param trie: The previously opened index being finalized
+ *
+ * Finalizes an index file that was being created, and frees the memory involved
+ * *trie is set to NULL afterwards.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_fts_destroy(struct lws_fts **trie);
+
+/**
+ * lws_fts_file_index() - Create a new entry in the trie file for an input path
+ *
+ * \param t: The previously opened index being written
+ * \param filepath: The filepath (which may be virtual) associated with this file
+ * \param filepath_len: The number of chars in the filepath
+ * \param priority: not used yet
+ *
+ * Returns an ordinal that represents this new filepath in the index file.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_fts_file_index(struct lws_fts *t, const char *filepath, int filepath_len,
+ int priority);
+
+/**
+ * lws_fts_fill() - Process all or a bufferload of input file
+ *
+ * \param t: The previously opened index being written
+ * \param file_index: The ordinal representing this input filepath
+ * \param buf: A bufferload of data from the input file
+ * \param len: The number of bytes in buf
+ *
+ * Indexes a buffer of data from the input file.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_fts_fill(struct lws_fts *t, uint32_t file_index, const char *buf,
+ size_t len);
+
+/**
+ * lws_fts_serialize() - Store the in-memory trie into the index file
+ *
+ * \param t: The previously opened index being written
+ *
+ * The trie is held in memory where it can be added to... after all the input
+ * filepaths and data have been processed, this is called to serialize /
+ * write the trie data into the index file.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_fts_serialize(struct lws_fts *t);
+
+/*
+ * index search functions
+ */
+
+/**
+ * lws_fts_open() - Open an existing index file to search it
+ *
+ * \param filepath: The filepath to the index file to open
+ *
+ * Opening the index file returns an opaque struct lws_fts_file * that is
+ * used to perform other operations on it, or NULL if it can't be opened.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_fts_file *
+lws_fts_open(const char *filepath);
+
+#define LWSFTS_F_QUERY_AUTOCOMPLETE (1 << 0)
+#define LWSFTS_F_QUERY_FILES (1 << 1)
+#define LWSFTS_F_QUERY_FILE_LINES (1 << 2)
+#define LWSFTS_F_QUERY_QUOTE_LINE (1 << 3)
+
+struct lws_fts_search_params {
+ /* the actual search term */
+ const char *needle;
+ /* if non-NULL, FILE results for this filepath only */
+ const char *only_filepath;
+ /* will be set to the results lwsac */
+ struct lwsac *results_head;
+ /* combination of LWSFTS_F_QUERY_* flags */
+ int flags;
+ /* maximum number of autocomplete suggestions to return */
+ int max_autocomplete;
+ /* maximum number of filepaths to return */
+ int max_files;
+ /* maximum number of line number results to return per filepath */
+ int max_lines;
+};
+
+/**
+ * lws_fts_search() - Perform a search operation on an index
+ *
+ * \param jtf: The index file struct returned by lws_fts_open
+ * \param ftsp: The struct lws_fts_search_params filled in by the caller
+ *
+ * The caller should memset the ftsp struct to 0 to ensure members that may be
+ * introduced in later versions contain known values, then set the related
+ * members to describe the kind of search action required.
+ *
+ * ftsp->results_head is the results lwsac, or NULL. It should be freed with
+ * lwsac_free() when the results are finished with.
+ *
+ * Returns a pointer into the results lwsac that is a struct lws_fts_result
+ * containing the head pointers into linked-lists of results for autocomplete
+ * and filepath data, along with some sundry information. This does not need
+ * to be freed since freeing the lwsac will also remove this and everything it
+ * points to.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_fts_result *
+lws_fts_search(struct lws_fts_file *jtf, struct lws_fts_search_params *ftsp);
+
+/**
+ * lws_fts_close() - Close a previously-opened index file
+ *
+ * \param jtf: The pointer returned from the open
+ *
+ * Closes the file handle on the index and frees any allocations
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_fts_close(struct lws_fts_file *jtf);
+
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*! \defgroup generic AES
+ * ## Generic AES related functions
+ *
+ * Lws provides generic AES functions that abstract the ones
+ * provided by whatever tls library you are linking against.
+ *
+ * It lets you use the same code if you build against mbedtls or OpenSSL
+ * for example.
+ */
+///@{
+
+#if defined(LWS_WITH_MBEDTLS)
+#include <mbedtls/aes.h>
+#include <mbedtls/gcm.h>
+#endif
+
+enum enum_aes_modes {
+ LWS_GAESM_CBC,
+ LWS_GAESM_CFB128,
+ LWS_GAESM_CFB8,
+ LWS_GAESM_CTR,
+ LWS_GAESM_ECB,
+ LWS_GAESM_OFB,
+ LWS_GAESM_XTS, /* care... requires double-length key */
+ LWS_GAESM_GCM,
+ LWS_GAESM_KW,
+};
+
+enum enum_aes_operation {
+ LWS_GAESO_ENC,
+ LWS_GAESO_DEC
+};
+
+enum enum_aes_padding {
+ LWS_GAESP_NO_PADDING,
+ LWS_GAESP_WITH_PADDING
+};
+
+/* include/libwebsockets/lws-jwk.h must be included before this */
+
+#define LWS_AES_BLOCKSIZE 128
+#define LWS_AES_CBC_BLOCKLEN 16
+
+struct lws_genaes_ctx {
+#if defined(LWS_WITH_MBEDTLS)
+ union {
+ mbedtls_aes_context ctx;
+#if defined(MBEDTLS_CIPHER_MODE_XTS)
+ mbedtls_aes_xts_context ctx_xts;
+#endif
+ mbedtls_gcm_context ctx_gcm;
+ } u;
+#else
+ EVP_CIPHER_CTX *ctx;
+ const EVP_CIPHER *cipher;
+ ENGINE *engine;
+ char init;
+#endif
+ unsigned char tag[16];
+ struct lws_gencrypto_keyelem *k;
+ enum enum_aes_operation op;
+ enum enum_aes_modes mode;
+ enum enum_aes_padding padding;
+ int taglen;
+ char underway;
+};
+
+/** lws_genaes_create() - Create RSA public decrypt context
+ *
+ * \param ctx: your struct lws_genaes_ctx
+ * \param op: LWS_GAESO_ENC or LWS_GAESO_DEC
+ * \param mode: one of LWS_GAESM_
+ * \param el: struct prepared with key element data
+ * \param padding: 0 = no padding, 1 = padding
+ * \param engine: if openssl engine used, pass the pointer here
+ *
+ * Creates an RSA context with a public key associated with it, formed from
+ * the key elements in \p el.
+ *
+ * Returns 0 for OK or nonzero for error.
+ *
+ * This and related APIs operate identically with OpenSSL or mbedTLS backends.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
+ enum enum_aes_modes mode, struct lws_gencrypto_keyelem *el,
+ enum enum_aes_padding padding, void *engine);
+
+/** lws_genaes_destroy() - Destroy genaes AES context
+ *
+ * \param ctx: your struct lws_genaes_ctx
+ * \param tag: NULL, or, GCM-only: buffer to receive tag
+ * \param tlen: 0, or, GCM-only: length of tag buffer
+ *
+ * Destroys any allocations related to \p ctx.
+ *
+ * For GCM only, up to tlen bytes of tag buffer will be set on exit.
+ *
+ * This and related APIs operate identically with OpenSSL or mbedTLS backends.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genaes_destroy(struct lws_genaes_ctx *ctx, unsigned char *tag, size_t tlen);
+
+/** lws_genaes_crypt() - Encrypt or decrypt
+ *
+ * \param ctx: your struct lws_genaes_ctx
+ * \param in: input plaintext or ciphertext
+ * \param len: length of input (which is always length of output)
+ * \param out: output plaintext or ciphertext
+ * \param iv_or_nonce_ctr_or_data_unit_16: NULL, iv, nonce_ctr16, or data_unit16
+ * \param stream_block_16: pointer to 16-byte stream block for CTR mode only
+ * \param nc_or_iv_off: NULL or pointer to nc, or iv_off
+ * \param taglen: length of tag
+ *
+ * Encrypts or decrypts using the AES mode set when the ctx was created.
+ * The last three arguments have different meanings depending on the mode:
+ *
+ * KW CBC CFB128 CFB8 CTR ECB OFB XTS
+ * iv_or_nonce_ct.._unit_16 : iv iv iv iv nonce NULL iv dataunt
+ * stream_block_16 : NULL NULL NULL NULL stream NULL NULL NULL
+ * nc_or_iv_off : NULL NULL iv_off NULL nc_off NULL iv_off NULL
+ *
+ * For GCM:
+ *
+ * iv_or_nonce_ctr_or_data_unit_16 : iv
+ * stream_block_16 : pointer to tag
+ * nc_or_iv_off : set pointed-to size_t to iv length
+ * in : first call: additional data, subsequently
+ * : input data
+ * len : first call: add data length, subsequently
+ * : input / output length
+ *
+ * The length of the optional arg is always 16 if used, regardless of the mode.
+ *
+ * Returns 0 for OK or nonzero for error.
+ *
+ * This and related APIs operate identically with OpenSSL or mbedTLS backends.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genaes_crypt(struct lws_genaes_ctx *ctx, const uint8_t *in, size_t len,
+ uint8_t *out,
+ uint8_t *iv_or_nonce_ctr_or_data_unit_16,
+ uint8_t *stream_block_16,
+ size_t *nc_or_iv_off, int taglen);
+
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*
+ * These are gencrypto-level constants... they are used by both JOSE and direct
+ * gencrypto code. However while JWK relies on these, using gencrypto apis has
+ * no dependency at all on any JOSE type.
+ */
+
+enum lws_gencrypto_kty {
+ LWS_GENCRYPTO_KTY_UNKNOWN,
+
+ LWS_GENCRYPTO_KTY_OCT,
+ LWS_GENCRYPTO_KTY_RSA,
+ LWS_GENCRYPTO_KTY_EC
+};
+
+/*
+ * Keytypes where the same element name is reused must all agree to put the
+ * same-named element at the same e[] index. It's because when used with jwk,
+ * we parse and store in incoming key data, but we may not be informed of the
+ * definitive keytype until the end.
+ */
+
+enum lws_gencrypto_oct_tok {
+ LWS_GENCRYPTO_OCT_KEYEL_K, /* note... same offset as AES K */
+
+ LWS_GENCRYPTO_OCT_KEYEL_COUNT
+};
+
+enum lws_gencrypto_rsa_tok {
+ LWS_GENCRYPTO_RSA_KEYEL_E,
+ LWS_GENCRYPTO_RSA_KEYEL_N,
+ LWS_GENCRYPTO_RSA_KEYEL_D, /* note... same offset as EC D */
+ LWS_GENCRYPTO_RSA_KEYEL_P,
+ LWS_GENCRYPTO_RSA_KEYEL_Q,
+ LWS_GENCRYPTO_RSA_KEYEL_DP,
+ LWS_GENCRYPTO_RSA_KEYEL_DQ,
+ LWS_GENCRYPTO_RSA_KEYEL_QI,
+
+ LWS_GENCRYPTO_RSA_KEYEL_COUNT
+};
+
+enum lws_gencrypto_ec_tok {
+ LWS_GENCRYPTO_EC_KEYEL_CRV,
+ LWS_GENCRYPTO_EC_KEYEL_X,
+ /* note... same offset as RSA D */
+ LWS_GENCRYPTO_EC_KEYEL_D = LWS_GENCRYPTO_RSA_KEYEL_D,
+ LWS_GENCRYPTO_EC_KEYEL_Y,
+
+ LWS_GENCRYPTO_EC_KEYEL_COUNT
+};
+
+enum lws_gencrypto_aes_tok {
+ /* note... same offset as OCT K */
+ LWS_GENCRYPTO_AES_KEYEL_K = LWS_GENCRYPTO_OCT_KEYEL_K,
+
+ LWS_GENCRYPTO_AES_KEYEL_COUNT
+};
+
+/* largest number of key elements for any algorithm */
+#define LWS_GENCRYPTO_MAX_KEYEL_COUNT LWS_GENCRYPTO_RSA_KEYEL_COUNT
+
+/* this "stretchy" type holds individual key element data in binary form.
+ * It's typcially used in an array with the layout mapping the element index to
+ * the key element meaning defined by the enums above. An array of these of
+ * length LWS_GENCRYPTO_MAX_KEYEL_COUNT can define key elements for any key
+ * type.
+ */
+
+struct lws_gencrypto_keyelem {
+ uint8_t *buf;
+ uint32_t len;
+};
+
+
+/**
+ * lws_gencrypto_bits_to_bytes() - returns rounded up bytes needed for bits
+ *
+ * \param bits
+ *
+ * Returns the number of bytes needed to store the given number of bits. If
+ * a byte is partially used, the byte count is rounded up.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_gencrypto_bits_to_bytes(int bits);
+
+/**
+ * lws_base64_size() - returns estimated size of base64 encoding
+ *
+ * \param bytes
+ *
+ * Returns a slightly oversize estimate of the size of a base64 encoded version
+ * of the given amount of unencoded data.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_base64_size(int bytes);
+
+/**
+ * lws_gencrypto_padded_length() - returns PKCS#5/#7 padded length
+ *
+ * @param blocksize - blocksize to pad to
+ * @param len - Length of input to pad
+ *
+ * Returns the length of a buffer originally of size len after PKCS#5 or PKCS#7
+ * padding has been applied to it.
+ */
+LWS_VISIBLE LWS_EXTERN size_t
+lws_gencrypto_padded_length(size_t block_size, size_t len);
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+enum enum_genec_alg {
+ LEGENEC_UNKNOWN,
+
+ LEGENEC_ECDH,
+ LEGENEC_ECDSA
+};
+
+struct lws_genec_ctx {
+#if defined(LWS_WITH_MBEDTLS)
+ union {
+ mbedtls_ecdh_context *ctx_ecdh;
+ mbedtls_ecdsa_context *ctx_ecdsa;
+ } u;
+#else
+ EVP_PKEY_CTX *ctx[2];
+#endif
+ struct lws_context *context;
+ const struct lws_ec_curves *curve_table;
+ enum enum_genec_alg genec_alg;
+
+ char has_private;
+};
+
+#if defined(LWS_WITH_MBEDTLS)
+enum enum_lws_dh_side {
+ LDHS_OURS = MBEDTLS_ECDH_OURS,
+ LDHS_THEIRS = MBEDTLS_ECDH_THEIRS
+};
+#else
+enum enum_lws_dh_side {
+ LDHS_OURS,
+ LDHS_THEIRS
+};
+#endif
+
+struct lws_ec_curves {
+ const char *name;
+ int tls_lib_nid;
+ uint16_t key_bytes;
+};
+
+
+/* ECDH-specific apis */
+
+/** lws_genecdh_create() - Create a genecdh
+ *
+ * \param ctx: your genec context
+ * \param context: your lws_context (for RNG access)
+ * \param curve_table: NULL, enabling P-256, P-384 and P-521, or a replacement
+ * struct lws_ec_curves array, terminated by an entry with
+ * .name = NULL, of curves you want to whitelist
+ *
+ * Initializes a genecdh
+ */
+LWS_VISIBLE int
+lws_genecdh_create(struct lws_genec_ctx *ctx, struct lws_context *context,
+ const struct lws_ec_curves *curve_table);
+
+/** lws_genecdh_set_key() - Apply an EC key to our or theirs side
+ *
+ * \param ctx: your genecdh context
+ * \param el: your key elements
+ * \param side: LDHS_OURS or LDHS_THEIRS
+ *
+ * Applies an EC key to one side or the other of an ECDH ctx
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genecdh_set_key(struct lws_genec_ctx *ctx, struct lws_gencrypto_keyelem *el,
+ enum enum_lws_dh_side side);
+
+/** lws_genecdh_new_keypair() - Create a genec with a new public / private key
+ *
+ * \param ctx: your genec context
+ * \param side: LDHS_OURS or LDHS_THEIRS
+ * \param curve_name: an EC curve name, like "P-256"
+ * \param el: array pf LWS_GENCRYPTO_EC_KEYEL_COUNT key elems to take the new key
+ *
+ * Creates a genecdh with a newly minted EC public / private key
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genecdh_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
+ const char *curve_name, struct lws_gencrypto_keyelem *el);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_genecdh_compute_shared_secret(struct lws_genec_ctx *ctx, uint8_t *ss,
+ int *ss_len);
+
+
+/* ECDSA-specific apis */
+
+/** lws_genecdsa_create() - Create a genecdsa and
+ *
+ * \param ctx: your genec context
+ * \param context: your lws_context (for RNG access)
+ * \param curve_table: NULL, enabling P-256, P-384 and P-521, or a replacement
+ * struct lws_ec_curves array, terminated by an entry with
+ * .name = NULL, of curves you want to whitelist
+ *
+ * Initializes a genecdh
+ */
+LWS_VISIBLE int
+lws_genecdsa_create(struct lws_genec_ctx *ctx, struct lws_context *context,
+ const struct lws_ec_curves *curve_table);
+
+/** lws_genecdsa_new_keypair() - Create a genecdsa with a new public / private key
+ *
+ * \param ctx: your genec context
+ * \param curve_name: an EC curve name, like "P-256"
+ * \param el: array pf LWS_GENCRYPTO_EC_KEYEL_COUNT key elements to take the new key
+ *
+ * Creates a genecdsa with a newly minted EC public / private key
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genecdsa_new_keypair(struct lws_genec_ctx *ctx, const char *curve_name,
+ struct lws_gencrypto_keyelem *el);
+
+/** lws_genecdsa_set_key() - Apply an EC key to an ecdsa context
+ *
+ * \param ctx: your genecdsa context
+ * \param el: your key elements
+ *
+ * Applies an EC key to an ecdsa context
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genecdsa_set_key(struct lws_genec_ctx *ctx,
+ struct lws_gencrypto_keyelem *el);
+
+/** lws_genecdsa_hash_sig_verify_jws() - Verifies a JWS ECDSA signature on a given hash
+ *
+ * \param ctx: your struct lws_genrsa_ctx
+ * \param in: unencrypted payload (usually a recomputed hash)
+ * \param hash_type: one of LWS_GENHASH_TYPE_
+ * \param keybits: number of bits in the crypto key
+ * \param sig: pointer to the signature we received with the payload
+ * \param sig_len: length of the signature we are checking in bytes
+ *
+ * This just looks at the signed hash... that's why there's no input length
+ * parameter, it's decided by the choice of hash. It's up to you to confirm
+ * separately the actual payload matches the hash that was confirmed by this to
+ * be validly signed.
+ *
+ * Returns <0 for error, or 0 if signature matches the hash + key..
+ *
+ * The JWS ECDSA signature verification algorithm differs to generic ECDSA
+ * signatures and they're not interoperable.
+ *
+ * This and related APIs operate identically with OpenSSL or mbedTLS backends.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genecdsa_hash_sig_verify_jws(struct lws_genec_ctx *ctx, const uint8_t *in,
+ enum lws_genhash_types hash_type, int keybits,
+ const uint8_t *sig, size_t sig_len);
+
+/** lws_genecdsa_hash_sign_jws() - Creates a JWS ECDSA signature for a hash you provide
+ *
+ * \param ctx: your struct lws_genrsa_ctx
+ * \param in: precomputed hash
+ * \param hash_type: one of LWS_GENHASH_TYPE_
+ * \param keybits: number of bits in the crypto key
+ * \param sig: pointer to buffer to take signature
+ * \param sig_len: length of the buffer (must be >= length of key N)
+ *
+ * Returns <0 for error, or 0 for success.
+ *
+ * This creates a JWS ECDSA signature for a hash you already computed and provide.
+ *
+ * The JWS ECDSA signature generation algorithm differs to generic ECDSA
+ * signatures and they're not interoperable.
+ *
+ * This and related APIs operate identically with OpenSSL or mbedTLS backends.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genecdsa_hash_sign_jws(struct lws_genec_ctx *ctx, const uint8_t *in,
+ enum lws_genhash_types hash_type, int keybits,
+ uint8_t *sig, size_t sig_len);
+
+
+/* Apis that apply to both ECDH and ECDSA */
+
+LWS_VISIBLE LWS_EXTERN void
+lws_genec_destroy(struct lws_genec_ctx *ctx);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_genec_destroy_elements(struct lws_gencrypto_keyelem *el);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_genec_dump(struct lws_gencrypto_keyelem *el);
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*! \defgroup generichash Generic Hash
+ * ## Generic Hash related functions
+ *
+ * Lws provides generic hash / digest accessors that abstract the ones
+ * provided by whatever tls library you are linking against.
+ *
+ * It lets you use the same code if you build against mbedtls or OpenSSL
+ * for example.
+ */
+///@{
+
+enum lws_genhash_types {
+ LWS_GENHASH_TYPE_UNKNOWN,
+ LWS_GENHASH_TYPE_MD5,
+ LWS_GENHASH_TYPE_SHA1,
+ LWS_GENHASH_TYPE_SHA256,
+ LWS_GENHASH_TYPE_SHA384,
+ LWS_GENHASH_TYPE_SHA512,
+};
+
+enum lws_genhmac_types {
+ LWS_GENHMAC_TYPE_UNKNOWN,
+ LWS_GENHMAC_TYPE_SHA256,
+ LWS_GENHMAC_TYPE_SHA384,
+ LWS_GENHMAC_TYPE_SHA512,
+};
+
+#define LWS_GENHASH_LARGEST 64
+
+struct lws_genhash_ctx {
+ uint8_t type;
+#if defined(LWS_WITH_MBEDTLS)
+ union {
+ mbedtls_md5_context md5;
+ mbedtls_sha1_context sha1;
+ mbedtls_sha256_context sha256;
+ mbedtls_sha512_context sha512; /* 384 also uses this */
+ const mbedtls_md_info_t *hmac;
+ } u;
+#else
+ const EVP_MD *evp_type;
+ EVP_MD_CTX *mdctx;
+#endif
+};
+
+struct lws_genhmac_ctx {
+ uint8_t type;
+#if defined(LWS_WITH_MBEDTLS)
+ const mbedtls_md_info_t *hmac;
+ mbedtls_md_context_t ctx;
+#else
+ const EVP_MD *evp_type;
+#if defined(LWS_HAVE_HMAC_CTX_new)
+ HMAC_CTX *ctx;
+#else
+ HMAC_CTX ctx;
+#endif
+#endif
+};
+
+/** lws_genhash_size() - get hash size in bytes
+ *
+ * \param type: one of LWS_GENHASH_TYPE_...
+ *
+ * Returns number of bytes in this type of hash
+ */
+LWS_VISIBLE LWS_EXTERN size_t LWS_WARN_UNUSED_RESULT
+lws_genhash_size(enum lws_genhash_types type);
+
+/** lws_genhmac_size() - get hash size in bytes
+ *
+ * \param type: one of LWS_GENHASH_TYPE_...
+ *
+ * Returns number of bytes in this type of hmac
+ */
+LWS_VISIBLE LWS_EXTERN size_t LWS_WARN_UNUSED_RESULT
+lws_genhmac_size(enum lws_genhmac_types type);
+
+/** lws_genhash_init() - prepare your struct lws_genhash_ctx for use
+ *
+ * \param ctx: your struct lws_genhash_ctx
+ * \param type: one of LWS_GENHASH_TYPE_...
+ *
+ * Initializes the hash context for the type you requested
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_genhash_init(struct lws_genhash_ctx *ctx, enum lws_genhash_types type);
+
+/** lws_genhash_update() - digest len bytes of the buffer starting at in
+ *
+ * \param ctx: your struct lws_genhash_ctx
+ * \param in: start of the bytes to digest
+ * \param len: count of bytes to digest
+ *
+ * Updates the state of your hash context to reflect digesting len bytes from in
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_genhash_update(struct lws_genhash_ctx *ctx, const void *in, size_t len);
+
+/** lws_genhash_destroy() - copy out the result digest and destroy the ctx
+ *
+ * \param ctx: your struct lws_genhash_ctx
+ * \param result: NULL, or where to copy the result hash
+ *
+ * Finalizes the hash and copies out the digest. Destroys any allocations such
+ * that ctx can safely go out of scope after calling this.
+ *
+ * NULL result is supported so that you can destroy the ctx cleanly on error
+ * conditions, where there is no valid result.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genhash_destroy(struct lws_genhash_ctx *ctx, void *result);
+
+/** lws_genhmac_init() - prepare your struct lws_genhmac_ctx for use
+ *
+ * \param ctx: your struct lws_genhmac_ctx
+ * \param type: one of LWS_GENHMAC_TYPE_...
+ * \param key: pointer to the start of the HMAC key
+ * \param key_len: length of the HMAC key
+ *
+ * Initializes the hash context for the type you requested
+ *
+ * If the return is nonzero, it failed and there is nothing needing to be
+ * destroyed.
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_genhmac_init(struct lws_genhmac_ctx *ctx, enum lws_genhmac_types type,
+ const uint8_t *key, size_t key_len);
+
+/** lws_genhmac_update() - digest len bytes of the buffer starting at in
+ *
+ * \param ctx: your struct lws_genhmac_ctx
+ * \param in: start of the bytes to digest
+ * \param len: count of bytes to digest
+ *
+ * Updates the state of your hash context to reflect digesting len bytes from in
+ *
+ * If the return is nonzero, it failed and needs destroying.
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_genhmac_update(struct lws_genhmac_ctx *ctx, const void *in, size_t len);
+
+/** lws_genhmac_destroy() - copy out the result digest and destroy the ctx
+ *
+ * \param ctx: your struct lws_genhmac_ctx
+ * \param result: NULL, or where to copy the result hash
+ *
+ * Finalizes the hash and copies out the digest. Destroys any allocations such
+ * that ctx can safely go out of scope after calling this.
+ *
+ * NULL result is supported so that you can destroy the ctx cleanly on error
+ * conditions, where there is no valid result.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genhmac_destroy(struct lws_genhmac_ctx *ctx, void *result);
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*! \defgroup genericRSA Generic RSA
+ * ## Generic RSA related functions
+ *
+ * Lws provides generic RSA functions that abstract the ones
+ * provided by whatever OpenSSL library you are linking against.
+ *
+ * It lets you use the same code if you build against mbedtls or OpenSSL
+ * for example.
+ */
+///@{
+
+/* include/libwebsockets/lws-jwk.h must be included before this */
+
+enum enum_genrsa_mode {
+ LGRSAM_PKCS1_1_5,
+ LGRSAM_PKCS1_OAEP_PSS,
+
+ LGRSAM_COUNT
+};
+
+struct lws_genrsa_ctx {
+#if defined(LWS_WITH_MBEDTLS)
+ mbedtls_rsa_context *ctx;
+#else
+ BIGNUM *bn[LWS_GENCRYPTO_RSA_KEYEL_COUNT];
+ EVP_PKEY_CTX *ctx;
+ RSA *rsa;
+#endif
+ struct lws_context *context;
+ enum enum_genrsa_mode mode;
+};
+
+/** lws_genrsa_public_decrypt_create() - Create RSA public decrypt context
+ *
+ * \param ctx: your struct lws_genrsa_ctx
+ * \param el: struct prepared with key element data
+ * \param context: lws_context for RNG
+ * \param mode: RSA mode, one of LGRSAM_ constants
+ * \param oaep_hashid: the lws genhash id for the hash used in MFG1 hash
+ * used in OAEP mode - normally, SHA1
+ *
+ * Creates an RSA context with a public key associated with it, formed from
+ * the key elements in \p el.
+ *
+ * Mode LGRSAM_PKCS1_1_5 is in widespread use but has weaknesses. It's
+ * recommended to use LGRSAM_PKCS1_OAEP_PSS for new implementations.
+ *
+ * Returns 0 for OK or nonzero for error.
+ *
+ * This and related APIs operate identically with OpenSSL or mbedTLS backends.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_gencrypto_keyelem *el,
+ struct lws_context *context, enum enum_genrsa_mode mode,
+ enum lws_genhash_types oaep_hashid);
+
+/** lws_genrsa_destroy_elements() - Free allocations in genrsa_elements
+ *
+ * \param el: your struct lws_gencrypto_keyelem
+ *
+ * This is a helper for user code making use of struct lws_gencrypto_keyelem
+ * where the elements are allocated on the heap, it frees any non-NULL
+ * buf element and sets the buf to NULL.
+ *
+ * NB: lws_genrsa_public_... apis do not need this as they take care of the key
+ * creation and destruction themselves.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_genrsa_destroy_elements(struct lws_gencrypto_keyelem *el);
+
+/** lws_genrsa_new_keypair() - Create new RSA keypair
+ *
+ * \param context: your struct lws_context (may be used for RNG)
+ * \param ctx: your struct lws_genrsa_ctx
+ * \param mode: RSA mode, one of LGRSAM_ constants
+ * \param el: struct to get the new key element data allocated into it
+ * \param bits: key size, eg, 4096
+ *
+ * Creates a new RSA context and generates a new keypair into it, with \p bits
+ * bits.
+ *
+ * Returns 0 for OK or nonzero for error.
+ *
+ * Mode LGRSAM_PKCS1_1_5 is in widespread use but has weaknesses. It's
+ * recommended to use LGRSAM_PKCS1_OAEP_PSS for new implementations.
+ *
+ * This and related APIs operate identically with OpenSSL or mbedTLS backends.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
+ enum enum_genrsa_mode mode, struct lws_gencrypto_keyelem *el,
+ int bits);
+
+/** lws_genrsa_public_encrypt() - Perform RSA public key encryption
+ *
+ * \param ctx: your struct lws_genrsa_ctx
+ * \param in: plaintext input
+ * \param in_len: length of plaintext input
+ * \param out: encrypted output
+ *
+ * Performs PKCS1 v1.5 Encryption
+ *
+ * Returns <0 for error, or length of decrypted data.
+ *
+ * This and related APIs operate identically with OpenSSL or mbedTLS backends.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+ size_t in_len, uint8_t *out);
+
+/** lws_genrsa_private_encrypt() - Perform RSA private key encryption
+ *
+ * \param ctx: your struct lws_genrsa_ctx
+ * \param in: plaintext input
+ * \param in_len: length of plaintext input
+ * \param out: encrypted output
+ *
+ * Performs PKCS1 v1.5 Encryption
+ *
+ * Returns <0 for error, or length of decrypted data.
+ *
+ * This and related APIs operate identically with OpenSSL or mbedTLS backends.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genrsa_private_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+ size_t in_len, uint8_t *out);
+
+/** lws_genrsa_public_decrypt() - Perform RSA public key decryption
+ *
+ * \param ctx: your struct lws_genrsa_ctx
+ * \param in: encrypted input
+ * \param in_len: length of encrypted input
+ * \param out: decrypted output
+ * \param out_max: size of output buffer
+ *
+ * Performs PKCS1 v1.5 Decryption
+ *
+ * Returns <0 for error, or length of decrypted data.
+ *
+ * This and related APIs operate identically with OpenSSL or mbedTLS backends.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genrsa_public_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+ size_t in_len, uint8_t *out, size_t out_max);
+
+/** lws_genrsa_private_decrypt() - Perform RSA private key decryption
+ *
+ * \param ctx: your struct lws_genrsa_ctx
+ * \param in: encrypted input
+ * \param in_len: length of encrypted input
+ * \param out: decrypted output
+ * \param out_max: size of output buffer
+ *
+ * Performs PKCS1 v1.5 Decryption
+ *
+ * Returns <0 for error, or length of decrypted data.
+ *
+ * This and related APIs operate identically with OpenSSL or mbedTLS backends.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genrsa_private_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+ size_t in_len, uint8_t *out, size_t out_max);
+
+/** lws_genrsa_hash_sig_verify() - Verifies RSA signature on a given hash
+ *
+ * \param ctx: your struct lws_genrsa_ctx
+ * \param in: input to be hashed
+ * \param hash_type: one of LWS_GENHASH_TYPE_
+ * \param sig: pointer to the signature we received with the payload
+ * \param sig_len: length of the signature we are checking in bytes
+ *
+ * Returns <0 for error, or 0 if signature matches the payload + key.
+ *
+ * This just looks at a hash... that's why there's no input length
+ * parameter, it's decided by the choice of hash. It's up to you to confirm
+ * separately the actual payload matches the hash that was confirmed by this to
+ * be validly signed.
+ *
+ * This and related APIs operate identically with OpenSSL or mbedTLS backends.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genrsa_hash_sig_verify(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+ enum lws_genhash_types hash_type,
+ const uint8_t *sig, size_t sig_len);
+
+/** lws_genrsa_hash_sign() - Creates an ECDSA signature for a hash you provide
+ *
+ * \param ctx: your struct lws_genrsa_ctx
+ * \param in: input to be hashed and signed
+ * \param hash_type: one of LWS_GENHASH_TYPE_
+ * \param sig: pointer to buffer to take signature
+ * \param sig_len: length of the buffer (must be >= length of key N)
+ *
+ * Returns <0 for error, or 0 for success.
+ *
+ * This creates an RSA signature for a hash you already computed and provide.
+ * You should have created the hash before calling this by iterating over the
+ * actual payload you need to confirm.
+ *
+ * This and related APIs operate identically with OpenSSL or mbedTLS backends.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genrsa_hash_sign(struct lws_genrsa_ctx *ctx, const uint8_t *in,
+ enum lws_genhash_types hash_type,
+ uint8_t *sig, size_t sig_len);
+
+/** lws_genrsa_public_decrypt_destroy() - Destroy RSA public decrypt context
+ *
+ * \param ctx: your struct lws_genrsa_ctx
+ *
+ * Destroys any allocations related to \p ctx.
+ *
+ * This and related APIs operate identically with OpenSSL or mbedTLS backends.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_genrsa_destroy(struct lws_genrsa_ctx *ctx);
+
+/** lws_genrsa_render_pkey_asn1() - Exports public or private key to ASN1/DER
+ *
+ * \param ctx: your struct lws_genrsa_ctx
+ * \param _private: 0 = public part only, 1 = all parts of the key
+ * \param pkey_asn1: pointer to buffer to take the ASN1
+ * \param pkey_asn1_len: max size of the pkey_asn1_len
+ *
+ * Returns length of pkey_asn1 written, or -1 for error.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_genrsa_render_pkey_asn1(struct lws_genrsa_ctx *ctx, int _private,
+ uint8_t *pkey_asn1, size_t pkey_asn1_len);
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/* minimal space for typical headers and CSP stuff */
+
+#define LWS_RECOMMENDED_MIN_HEADER_SPACE 2048
+
+/*! \defgroup http HTTP
+
+ Modules related to handling HTTP
+*/
+//@{
+
+/*! \defgroup httpft HTTP File transfer
+ * \ingroup http
+
+ APIs for sending local files in response to HTTP requests
+*/
+//@{
+
+/**
+ * lws_get_mimetype() - Determine mimetype to use from filename
+ *
+ * \param file: filename
+ * \param m: NULL, or mount context
+ *
+ * This uses a canned list of known filetypes first, if no match and m is
+ * non-NULL, then tries a list of per-mount file suffix to mimtype mappings.
+ *
+ * Returns either NULL or a pointer to the mimetype matching the file.
+ */
+LWS_VISIBLE LWS_EXTERN const char *
+lws_get_mimetype(const char *file, const struct lws_http_mount *m);
+
+/**
+ * lws_serve_http_file() - Send a file back to the client using http
+ * \param wsi: Websocket instance (available from user callback)
+ * \param file: The file to issue over http
+ * \param content_type: The http content type, eg, text/html
+ * \param other_headers: NULL or pointer to header string
+ * \param other_headers_len: length of the other headers if non-NULL
+ *
+ * This function is intended to be called from the callback in response
+ * to http requests from the client. It allows the callback to issue
+ * local files down the http link in a single step.
+ *
+ * Returning <0 indicates error and the wsi should be closed. Returning
+ * >0 indicates the file was completely sent and
+ * lws_http_transaction_completed() called on the wsi (and close if != 0)
+ * ==0 indicates the file transfer is started and needs more service later,
+ * the wsi should be left alone.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
+ const char *other_headers, int other_headers_len);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_serve_http_file_fragment(struct lws *wsi);
+//@}
+
+
+enum http_status {
+ HTTP_STATUS_CONTINUE = 100,
+
+ HTTP_STATUS_OK = 200,
+ HTTP_STATUS_NO_CONTENT = 204,
+ HTTP_STATUS_PARTIAL_CONTENT = 206,
+
+ HTTP_STATUS_MOVED_PERMANENTLY = 301,
+ HTTP_STATUS_FOUND = 302,
+ HTTP_STATUS_SEE_OTHER = 303,
+ HTTP_STATUS_NOT_MODIFIED = 304,
+
+ HTTP_STATUS_BAD_REQUEST = 400,
+ HTTP_STATUS_UNAUTHORIZED,
+ HTTP_STATUS_PAYMENT_REQUIRED,
+ HTTP_STATUS_FORBIDDEN,
+ HTTP_STATUS_NOT_FOUND,
+ HTTP_STATUS_METHOD_NOT_ALLOWED,
+ HTTP_STATUS_NOT_ACCEPTABLE,
+ HTTP_STATUS_PROXY_AUTH_REQUIRED,
+ HTTP_STATUS_REQUEST_TIMEOUT,
+ HTTP_STATUS_CONFLICT,
+ HTTP_STATUS_GONE,
+ HTTP_STATUS_LENGTH_REQUIRED,
+ HTTP_STATUS_PRECONDITION_FAILED,
+ HTTP_STATUS_REQ_ENTITY_TOO_LARGE,
+ HTTP_STATUS_REQ_URI_TOO_LONG,
+ HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE,
+ HTTP_STATUS_REQ_RANGE_NOT_SATISFIABLE,
+ HTTP_STATUS_EXPECTATION_FAILED,
+
+ HTTP_STATUS_INTERNAL_SERVER_ERROR = 500,
+ HTTP_STATUS_NOT_IMPLEMENTED,
+ HTTP_STATUS_BAD_GATEWAY,
+ HTTP_STATUS_SERVICE_UNAVAILABLE,
+ HTTP_STATUS_GATEWAY_TIMEOUT,
+ HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
+};
+/*! \defgroup html-chunked-substitution HTML Chunked Substitution
+ * \ingroup http
+ *
+ * ##HTML chunked Substitution
+ *
+ * APIs for receiving chunks of text, replacing a set of variable names via
+ * a callback, and then prepending and appending HTML chunked encoding
+ * headers.
+ */
+//@{
+
+struct lws_process_html_args {
+ char *p; /**< pointer to the buffer containing the data */
+ int len; /**< length of the original data at p */
+ int max_len; /**< maximum length we can grow the data to */
+ int final; /**< set if this is the last chunk of the file */
+ int chunked; /**< 0 == unchunked, 1 == produce chunk headers
+ (incompatible with HTTP/2) */
+};
+
+typedef const char *(*lws_process_html_state_cb)(void *data, int index);
+
+struct lws_process_html_state {
+ char *start; /**< pointer to start of match */
+ char swallow[16]; /**< matched character buffer */
+ int pos; /**< position in match */
+ void *data; /**< opaque pointer */
+ const char * const *vars; /**< list of variable names */
+ int count_vars; /**< count of variable names */
+
+ lws_process_html_state_cb replace;
+ /**< called on match to perform substitution */
+};
+
+/*! lws_chunked_html_process() - generic chunked substitution
+ * \param args: buffer to process using chunked encoding
+ * \param s: current processing state
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_chunked_html_process(struct lws_process_html_args *args,
+ struct lws_process_html_state *s);
+//@}
+
+/** \defgroup HTTP-headers-read HTTP headers: read
+ * \ingroup http
+ *
+ * ##HTTP header releated functions
+ *
+ * In lws the client http headers are temporarily stored in a pool, only for the
+ * duration of the http part of the handshake. It's because in most cases,
+ * the header content is ignored for the whole rest of the connection lifetime
+ * and would then just be taking up space needlessly.
+ *
+ * During LWS_CALLBACK_HTTP when the URI path is delivered is the last time
+ * the http headers are still allocated, you can use these apis then to
+ * look at and copy out interesting header content (cookies, etc)
+ *
+ * Notice that the header total length reported does not include a terminating
+ * '\0', however you must allocate for it when using the _copy apis. So the
+ * length reported for a header containing "123" is 3, but you must provide
+ * a buffer of length 4 so that "123\0" may be copied into it, or the copy
+ * will fail with a nonzero return code.
+ *
+ * In the special case of URL arguments, like ?x=1&y=2, the arguments are
+ * stored in a token named for the method, eg, WSI_TOKEN_GET_URI if it
+ * was a GET or WSI_TOKEN_POST_URI if POST. You can check the total
+ * length to confirm the method.
+ *
+ * For URL arguments, each argument is stored urldecoded in a "fragment", so
+ * you can use the fragment-aware api lws_hdr_copy_fragment() to access each
+ * argument in turn: the fragments contain urldecoded strings like x=1 or y=2.
+ *
+ * As a convenience, lws has an api that will find the fragment with a
+ * given name= part, lws_get_urlarg_by_name().
+ */
+///@{
+
+/** struct lws_tokens
+ * you need these to look at headers that have been parsed if using the
+ * LWS_CALLBACK_FILTER_CONNECTION callback. If a header from the enum
+ * list below is absent, .token = NULL and len = 0. Otherwise .token
+ * points to .len chars containing that header content.
+ */
+struct lws_tokens {
+ unsigned char *token; /**< pointer to start of the token */
+ int len; /**< length of the token's value */
+};
+
+/* enum lws_token_indexes
+ * these have to be kept in sync with lextable.h / minilex.c
+ *
+ * NOTE: These public enums are part of the abi. If you want to add one,
+ * add it at where specified so existing users are unaffected.
+ */
+enum lws_token_indexes {
+ WSI_TOKEN_GET_URI, /* 0 */
+ WSI_TOKEN_POST_URI,
+#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
+ WSI_TOKEN_OPTIONS_URI,
+#endif
+ WSI_TOKEN_HOST,
+ WSI_TOKEN_CONNECTION,
+ WSI_TOKEN_UPGRADE, /* 5 */
+ WSI_TOKEN_ORIGIN,
+#if defined(LWS_ROLE_WS)
+ WSI_TOKEN_DRAFT,
+#endif
+ WSI_TOKEN_CHALLENGE,
+#if defined(LWS_ROLE_WS)
+ WSI_TOKEN_EXTENSIONS,
+ WSI_TOKEN_KEY1, /* 10 */
+ WSI_TOKEN_KEY2,
+ WSI_TOKEN_PROTOCOL,
+ WSI_TOKEN_ACCEPT,
+ WSI_TOKEN_NONCE,
+#endif
+ WSI_TOKEN_HTTP,
+#if defined(LWS_ROLE_H2)
+ WSI_TOKEN_HTTP2_SETTINGS, /* 16 */
+#endif
+ WSI_TOKEN_HTTP_ACCEPT,
+#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
+ WSI_TOKEN_HTTP_AC_REQUEST_HEADERS,
+#endif
+ WSI_TOKEN_HTTP_IF_MODIFIED_SINCE,
+ WSI_TOKEN_HTTP_IF_NONE_MATCH, /* 20 */
+ WSI_TOKEN_HTTP_ACCEPT_ENCODING,
+ WSI_TOKEN_HTTP_ACCEPT_LANGUAGE,
+ WSI_TOKEN_HTTP_PRAGMA,
+ WSI_TOKEN_HTTP_CACHE_CONTROL,
+ WSI_TOKEN_HTTP_AUTHORIZATION,
+ WSI_TOKEN_HTTP_COOKIE,
+ WSI_TOKEN_HTTP_CONTENT_LENGTH, /* 27 */
+ WSI_TOKEN_HTTP_CONTENT_TYPE,
+ WSI_TOKEN_HTTP_DATE,
+ WSI_TOKEN_HTTP_RANGE,
+#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2)
+ WSI_TOKEN_HTTP_REFERER,
+#endif
+#if defined(LWS_ROLE_WS)
+ WSI_TOKEN_KEY,
+ WSI_TOKEN_VERSION,
+ WSI_TOKEN_SWORIGIN,
+#endif
+#if defined(LWS_ROLE_H2)
+ WSI_TOKEN_HTTP_COLON_AUTHORITY,
+ WSI_TOKEN_HTTP_COLON_METHOD,
+ WSI_TOKEN_HTTP_COLON_PATH,
+ WSI_TOKEN_HTTP_COLON_SCHEME,
+ WSI_TOKEN_HTTP_COLON_STATUS,
+#endif
+
+#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2)
+ WSI_TOKEN_HTTP_ACCEPT_CHARSET,
+#endif
+ WSI_TOKEN_HTTP_ACCEPT_RANGES,
+#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2)
+ WSI_TOKEN_HTTP_ACCESS_CONTROL_ALLOW_ORIGIN,
+#endif
+ WSI_TOKEN_HTTP_AGE,
+ WSI_TOKEN_HTTP_ALLOW,
+ WSI_TOKEN_HTTP_CONTENT_DISPOSITION,
+ WSI_TOKEN_HTTP_CONTENT_ENCODING,
+ WSI_TOKEN_HTTP_CONTENT_LANGUAGE,
+ WSI_TOKEN_HTTP_CONTENT_LOCATION,
+ WSI_TOKEN_HTTP_CONTENT_RANGE,
+ WSI_TOKEN_HTTP_ETAG,
+ WSI_TOKEN_HTTP_EXPECT,
+ WSI_TOKEN_HTTP_EXPIRES,
+ WSI_TOKEN_HTTP_FROM,
+ WSI_TOKEN_HTTP_IF_MATCH,
+ WSI_TOKEN_HTTP_IF_RANGE,
+ WSI_TOKEN_HTTP_IF_UNMODIFIED_SINCE,
+ WSI_TOKEN_HTTP_LAST_MODIFIED,
+ WSI_TOKEN_HTTP_LINK,
+ WSI_TOKEN_HTTP_LOCATION,
+#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2)
+ WSI_TOKEN_HTTP_MAX_FORWARDS,
+ WSI_TOKEN_HTTP_PROXY_AUTHENTICATE,
+ WSI_TOKEN_HTTP_PROXY_AUTHORIZATION,
+#endif
+ WSI_TOKEN_HTTP_REFRESH,
+ WSI_TOKEN_HTTP_RETRY_AFTER,
+ WSI_TOKEN_HTTP_SERVER,
+ WSI_TOKEN_HTTP_SET_COOKIE,
+#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2)
+ WSI_TOKEN_HTTP_STRICT_TRANSPORT_SECURITY,
+#endif
+ WSI_TOKEN_HTTP_TRANSFER_ENCODING,
+#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2)
+ WSI_TOKEN_HTTP_USER_AGENT,
+ WSI_TOKEN_HTTP_VARY,
+ WSI_TOKEN_HTTP_VIA,
+ WSI_TOKEN_HTTP_WWW_AUTHENTICATE,
+#endif
+#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
+ WSI_TOKEN_PATCH_URI,
+ WSI_TOKEN_PUT_URI,
+ WSI_TOKEN_DELETE_URI,
+#endif
+
+ WSI_TOKEN_HTTP_URI_ARGS,
+#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
+ WSI_TOKEN_PROXY,
+ WSI_TOKEN_HTTP_X_REAL_IP,
+#endif
+ WSI_TOKEN_HTTP1_0,
+ WSI_TOKEN_X_FORWARDED_FOR,
+ WSI_TOKEN_CONNECT,
+ WSI_TOKEN_HEAD_URI,
+#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2)
+ WSI_TOKEN_TE,
+ WSI_TOKEN_REPLAY_NONCE, /* ACME */
+#endif
+#if defined(LWS_ROLE_H2)
+ WSI_TOKEN_COLON_PROTOCOL,
+#endif
+ WSI_TOKEN_X_AUTH_TOKEN,
+
+ /****** add new things just above ---^ ******/
+
+ /* use token storage to stash these internally, not for
+ * user use */
+
+ _WSI_TOKEN_CLIENT_SENT_PROTOCOLS,
+ _WSI_TOKEN_CLIENT_PEER_ADDRESS,
+ _WSI_TOKEN_CLIENT_URI,
+ _WSI_TOKEN_CLIENT_HOST,
+ _WSI_TOKEN_CLIENT_ORIGIN,
+ _WSI_TOKEN_CLIENT_METHOD,
+ _WSI_TOKEN_CLIENT_IFACE,
+ _WSI_TOKEN_CLIENT_ALPN,
+
+ /* always last real token index*/
+ WSI_TOKEN_COUNT,
+
+ /* parser state additions, no storage associated */
+ WSI_TOKEN_NAME_PART,
+#if defined(LWS_WITH_CUSTOM_HEADERS)
+ WSI_TOKEN_UNKNOWN_VALUE_PART,
+#endif
+ WSI_TOKEN_SKIPPING,
+ WSI_TOKEN_SKIPPING_SAW_CR,
+ WSI_PARSING_COMPLETE,
+ WSI_INIT_TOKEN_MUXURL,
+};
+
+struct lws_token_limits {
+ unsigned short token_limit[WSI_TOKEN_COUNT]; /**< max chars for this token */
+};
+
+enum lws_h2_settings {
+ H2SET_HEADER_TABLE_SIZE = 1,
+ H2SET_ENABLE_PUSH,
+ H2SET_MAX_CONCURRENT_STREAMS,
+ H2SET_INITIAL_WINDOW_SIZE,
+ H2SET_MAX_FRAME_SIZE,
+ H2SET_MAX_HEADER_LIST_SIZE,
+ H2SET_RESERVED7,
+ H2SET_ENABLE_CONNECT_PROTOCOL, /* defined in mcmanus-httpbis-h2-ws-02 */
+
+ H2SET_COUNT /* always last */
+};
+
+/**
+ * lws_token_to_string() - returns a textual representation of a hdr token index
+ *
+ * \param token: token index
+ */
+LWS_VISIBLE LWS_EXTERN const unsigned char *
+lws_token_to_string(enum lws_token_indexes token);
+
+/**
+ * lws_hdr_total_length: report length of all fragments of a header totalled up
+ * The returned length does not include the space for a
+ * terminating '\0'
+ *
+ * \param wsi: websocket connection
+ * \param h: which header index we are interested in
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_hdr_total_length(struct lws *wsi, enum lws_token_indexes h);
+
+/**
+ * lws_hdr_fragment_length: report length of a single fragment of a header
+ * The returned length does not include the space for a
+ * terminating '\0'
+ *
+ * \param wsi: websocket connection
+ * \param h: which header index we are interested in
+ * \param frag_idx: which fragment of h we want to get the length of
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_hdr_fragment_length(struct lws *wsi, enum lws_token_indexes h,
+ int frag_idx);
+
+/**
+ * lws_hdr_copy() - copy all fragments of the given header to a buffer
+ * The buffer length len must include space for an additional
+ * terminating '\0', or it will fail returning -1.
+ *
+ * \param wsi: websocket connection
+ * \param dest: destination buffer
+ * \param len: length of destination buffer
+ * \param h: which header index we are interested in
+ *
+ * copies the whole, aggregated header, even if it was delivered in
+ * several actual headers piece by piece. Returns -1 or length of the whole
+ * header.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_hdr_copy(struct lws *wsi, char *dest, int len, enum lws_token_indexes h);
+
+/**
+ * lws_hdr_copy_fragment() - copy a single fragment of the given header to a buffer
+ * The buffer length len must include space for an additional
+ * terminating '\0', or it will fail returning -1.
+ * If the requested fragment index is not present, it fails
+ * returning -1.
+ *
+ * \param wsi: websocket connection
+ * \param dest: destination buffer
+ * \param len: length of destination buffer
+ * \param h: which header index we are interested in
+ * \param frag_idx: which fragment of h we want to copy
+ *
+ * Normally this is only useful
+ * to parse URI arguments like ?x=1&y=2, token index WSI_TOKEN_HTTP_URI_ARGS
+ * fragment 0 will contain "x=1" and fragment 1 "y=2"
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_hdr_copy_fragment(struct lws *wsi, char *dest, int len,
+ enum lws_token_indexes h, int frag_idx);
+
+/**
+ * lws_hdr_custom_length() - return length of a custom header
+ *
+ * \param wsi: websocket connection
+ * \param name: header string (including terminating :)
+ * \param nlen: length of name
+ *
+ * Lws knows about 100 common http headers, and parses them into indexes when
+ * it recognizes them. When it meets a header that it doesn't know, it stores
+ * the name and value directly, and you can look them up using
+ * lws_hdr_custom_length() and lws_hdr_custom_copy().
+ *
+ * This api returns -1, or the length of the value part of the header if it
+ * exists. Lws must be built with LWS_WITH_CUSTOM_HEADERS (on by default) to
+ * use this api.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_hdr_custom_length(struct lws *wsi, const char *name, int nlen);
+
+/**
+ * lws_hdr_custom_copy() - copy value part of a custom header
+ *
+ * \param wsi: websocket connection
+ * \param dst: pointer to buffer to receive the copy
+ * \param len: number of bytes available at dst
+ * \param name: header string (including terminating :)
+ * \param nlen: length of name
+ *
+ * Lws knows about 100 common http headers, and parses them into indexes when
+ * it recognizes them. When it meets a header that it doesn't know, it stores
+ * the name and value directly, and you can look them up using
+ * lws_hdr_custom_length() and lws_hdr_custom_copy().
+ *
+ * This api returns -1, or the length of the string it copied into dst if it
+ * was big enough to contain both the string and an extra terminating NUL. Lws
+ * must be built with LWS_WITH_CUSTOM_HEADERS (on by default) to use this api.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_hdr_custom_copy(struct lws *wsi, char *dst, int len, const char *name,
+ int nlen);
+
+/**
+ * lws_get_urlarg_by_name() - return pointer to arg value if present
+ * \param wsi: the connection to check
+ * \param name: the arg name, like "token="
+ * \param buf: the buffer to receive the urlarg (including the name= part)
+ * \param len: the length of the buffer to receive the urlarg
+ *
+ * Returns NULL if not found or a pointer inside buf to just after the
+ * name= part.
+ */
+LWS_VISIBLE LWS_EXTERN const char *
+lws_get_urlarg_by_name(struct lws *wsi, const char *name, char *buf, int len);
+///@}
+
+/*! \defgroup HTTP-headers-create HTTP headers: create
+ *
+ * ## HTTP headers: Create
+ *
+ * These apis allow you to create HTTP response headers in a way compatible with
+ * both HTTP/1.x and HTTP/2.
+ *
+ * They each append to a buffer taking care about the buffer end, which is
+ * passed in as a pointer. When data is written to the buffer, the current
+ * position p is updated accordingly.
+ *
+ * All of these apis are LWS_WARN_UNUSED_RESULT as they can run out of space
+ * and fail with nonzero return.
+ */
+///@{
+
+#define LWSAHH_CODE_MASK ((1 << 16) - 1)
+#define LWSAHH_FLAG_NO_SERVER_NAME (1 << 30)
+
+/**
+ * lws_add_http_header_status() - add the HTTP response status code
+ *
+ * \param wsi: the connection to check
+ * \param code: an HTTP code like 200, 404 etc (see enum http_status)
+ * \param p: pointer to current position in buffer pointer
+ * \param end: pointer to end of buffer
+ *
+ * Adds the initial response code, so should be called first.
+ *
+ * Code may additionally take OR'd flags:
+ *
+ * LWSAHH_FLAG_NO_SERVER_NAME: don't apply server name header this time
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_add_http_header_status(struct lws *wsi,
+ unsigned int code, unsigned char **p,
+ unsigned char *end);
+/**
+ * lws_add_http_header_by_name() - append named header and value
+ *
+ * \param wsi: the connection to check
+ * \param name: the hdr name, like "my-header"
+ * \param value: the value after the = for this header
+ * \param length: the length of the value
+ * \param p: pointer to current position in buffer pointer
+ * \param end: pointer to end of buffer
+ *
+ * Appends name: value to the headers
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name,
+ const unsigned char *value, int length,
+ unsigned char **p, unsigned char *end);
+/**
+ * lws_add_http_header_by_token() - append given header and value
+ *
+ * \param wsi: the connection to check
+ * \param token: the token index for the hdr
+ * \param value: the value after the = for this header
+ * \param length: the length of the value
+ * \param p: pointer to current position in buffer pointer
+ * \param end: pointer to end of buffer
+ *
+ * Appends name=value to the headers, but is able to take advantage of better
+ * HTTP/2 coding mechanisms where possible.
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token,
+ const unsigned char *value, int length,
+ unsigned char **p, unsigned char *end);
+/**
+ * lws_add_http_header_content_length() - append content-length helper
+ *
+ * \param wsi: the connection to check
+ * \param content_length: the content length to use
+ * \param p: pointer to current position in buffer pointer
+ * \param end: pointer to end of buffer
+ *
+ * Appends content-length: content_length to the headers
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_add_http_header_content_length(struct lws *wsi,
+ lws_filepos_t content_length,
+ unsigned char **p, unsigned char *end);
+/**
+ * lws_finalize_http_header() - terminate header block
+ *
+ * \param wsi: the connection to check
+ * \param p: pointer to current position in buffer pointer
+ * \param end: pointer to end of buffer
+ *
+ * Indicates no more headers will be added
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_finalize_http_header(struct lws *wsi, unsigned char **p,
+ unsigned char *end);
+
+/**
+ * lws_finalize_write_http_header() - Helper finializing and writing http headers
+ *
+ * \param wsi: the connection to check
+ * \param start: pointer to the start of headers in the buffer, eg &buf[LWS_PRE]
+ * \param p: pointer to current position in buffer pointer
+ * \param end: pointer to end of buffer
+ *
+ * Terminates the headers correctly accoring to the protocol in use (h1 / h2)
+ * and writes the headers. Returns nonzero for error.
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_finalize_write_http_header(struct lws *wsi, unsigned char *start,
+ unsigned char **p, unsigned char *end);
+
+#define LWS_ILLEGAL_HTTP_CONTENT_LEN ((lws_filepos_t)-1ll)
+
+/**
+ * lws_add_http_common_headers() - Helper preparing common http headers
+ *
+ * \param wsi: the connection to check
+ * \param code: an HTTP code like 200, 404 etc (see enum http_status)
+ * \param content_type: the content type, like "text/html"
+ * \param content_len: the content length, in bytes
+ * \param p: pointer to current position in buffer pointer
+ * \param end: pointer to end of buffer
+ *
+ * Adds the initial response code, so should be called first.
+ *
+ * Code may additionally take OR'd flags:
+ *
+ * LWSAHH_FLAG_NO_SERVER_NAME: don't apply server name header this time
+ *
+ * This helper just calls public apis to simplify adding headers that are
+ * commonly needed. If it doesn't fit your case, or you want to add additional
+ * headers just call the public apis directly yourself for what you want.
+ *
+ * You can miss out the content length header by providing the constant
+ * LWS_ILLEGAL_HTTP_CONTENT_LEN for the content_len.
+ *
+ * It does not call lws_finalize_http_header(), to allow you to add further
+ * headers after calling this. You will need to call that yourself at the end.
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_add_http_common_headers(struct lws *wsi, unsigned int code,
+ const char *content_type, lws_filepos_t content_len,
+ unsigned char **p, unsigned char *end);
+
+enum {
+ LWSHUMETH_GET,
+ LWSHUMETH_POST,
+ LWSHUMETH_OPTIONS,
+ LWSHUMETH_PUT,
+ LWSHUMETH_PATCH,
+ LWSHUMETH_DELETE,
+ LWSHUMETH_CONNECT,
+ LWSHUMETH_HEAD,
+ LWSHUMETH_COLON_PATH,
+};
+
+/**
+ * lws_http_get_uri_and_method() - Get information on method and url
+ *
+ * \param wsi: the connection to get information on
+ * \param puri_ptr: points to pointer to set to url
+ * \param puri_len: points to int to set to uri length
+ *
+ * Returns -1 or method index as one of the LWSHUMETH_ constants
+ *
+ * If returns method, *puri_ptr is set to the method's URI string and *puri_len
+ * to its length
+ */
+
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len);
+
+///@}
+
+/*! \defgroup urlendec Urlencode and Urldecode
+ * \ingroup http
+ *
+ * ##HTML chunked Substitution
+ *
+ * APIs for receiving chunks of text, replacing a set of variable names via
+ * a callback, and then prepending and appending HTML chunked encoding
+ * headers.
+ */
+//@{
+
+/**
+ * lws_urlencode() - like strncpy but with urlencoding
+ *
+ * \param escaped: output buffer
+ * \param string: input buffer ('/0' terminated)
+ * \param len: output buffer max length
+ *
+ * Because urlencoding expands the output string, it's not
+ * possible to do it in-place, ie, with escaped == string
+ */
+LWS_VISIBLE LWS_EXTERN const char *
+lws_urlencode(char *escaped, const char *string, int len);
+
+/*
+ * URLDECODE 1 / 2
+ *
+ * This simple urldecode only operates until the first '\0' and requires the
+ * data to exist all at once
+ */
+/**
+ * lws_urldecode() - like strncpy but with urldecoding
+ *
+ * \param string: output buffer
+ * \param escaped: input buffer ('\0' terminated)
+ * \param len: output buffer max length
+ *
+ * This is only useful for '\0' terminated strings
+ *
+ * Since urldecoding only shrinks the output string, it is possible to
+ * do it in-place, ie, string == escaped
+ *
+ * Returns 0 if completed OK or nonzero for urldecode violation (non-hex chars
+ * where hex required, etc)
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_urldecode(char *string, const char *escaped, int len);
+///@}
+
+/**
+ * lws_return_http_status() - Return simple http status
+ * \param wsi: Websocket instance (available from user callback)
+ * \param code: Status index, eg, 404
+ * \param html_body: User-readable HTML description < 1KB, or NULL
+ *
+ * Helper to report HTTP errors back to the client cleanly and
+ * consistently
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_return_http_status(struct lws *wsi, unsigned int code,
+ const char *html_body);
+
+/**
+ * lws_http_redirect() - write http redirect out on wsi
+ *
+ * \param wsi: websocket connection
+ * \param code: HTTP response code (eg, 301)
+ * \param loc: where to redirect to
+ * \param len: length of loc
+ * \param p: pointer current position in buffer (updated as we write)
+ * \param end: pointer to end of buffer
+ *
+ * Returns amount written, or < 0 indicating fatal write failure.
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len,
+ unsigned char **p, unsigned char *end);
+
+/**
+ * lws_http_transaction_completed() - wait for new http transaction or close
+ * \param wsi: websocket connection
+ *
+ * Returns 1 if the HTTP connection must close now
+ * Returns 0 and resets connection to wait for new HTTP header /
+ * transaction if possible
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_http_transaction_completed(struct lws *wsi);
+
+/**
+ * lws_http_headers_detach() - drop the associated headers storage and allow
+ * it to be reused by another connection
+ * \param wsi: http connection
+ *
+ * If the wsi has an ah headers struct attached, detach it.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_http_headers_detach(struct lws *wsi);
+
+/**
+ * lws_http_mark_sse() - called to indicate this http stream is now doing SSE
+ *
+ * \param wsi: http connection
+ *
+ * Cancel any timeout on the wsi, and for h2, mark the network connection as
+ * containing an immortal stream for the duration the SSE stream is open.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_http_mark_sse(struct lws *wsi);
+
+/**
+ * lws_h2_client_stream_long_poll_rxonly() - h2 stream to immortal read-only
+ *
+ * \param wsi: h2 stream client wsi
+ *
+ * Send END_STREAM-flagged zero-length DATA frame to set client stream wsi into
+ * half-closed (local) and remote into half-closed (remote). Set the client
+ * stream wsi to be immortal (not subject to timeouts).
+ *
+ * Used if the remote server supports immortal long poll to put the stream into
+ * a read-only state where it can wait as long as needed for rx.
+ *
+ * Returns 0 if the process (which happens asynchronously) started or non-zero
+ * if it wasn't an h2 stream.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_h2_client_stream_long_poll_rxonly(struct lws *wsi);
+
+/**
+ * lws_http_compression_apply() - apply an http compression transform
+ *
+ * \param wsi: the wsi to apply the compression transform to
+ * \param name: NULL, or the name of the compression transform, eg, "deflate"
+ * \param p: pointer to pointer to headers buffer
+ * \param end: pointer to end of headers buffer
+ * \param decomp: 0 = add compressor to wsi, 1 = add decompressor
+ *
+ * This allows transparent compression of dynamically generated HTTP. The
+ * requested compression (eg, "deflate") is only applied if the client headers
+ * indicated it was supported (and it has support in lws), otherwise it's a NOP.
+ *
+ * If the requested compression method is NULL, then the supported compression
+ * formats are tried, and for non-decompression (server) mode the first that's
+ * found on the client's accept-encoding header is chosen.
+ *
+ * NOTE: the compression transform, same as h2 support, relies on the user
+ * code using LWS_WRITE_HTTP and then LWS_WRITE_HTTP_FINAL on the last part
+ * written. The internal lws fileserving code already does this.
+ *
+ * If the library was built without the cmake option
+ * LWS_WITH_HTTP_STREAM_COMPRESSION set, then a NOP is provided for this api,
+ * allowing user code to build either way and use compression if available.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_http_compression_apply(struct lws *wsi, const char *name,
+ unsigned char **p, unsigned char *end, char decomp);
+
+/**
+ * lws_http_is_redirected_to_get() - true if redirected to GET
+ *
+ * \param wsi: the wsi to check
+ *
+ * Check if the wsi is currently in GET mode, after, eg, doing a POST and
+ * receiving a 303.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_http_is_redirected_to_get(struct lws *wsi);
+
+/**
+ * lws_h2_update_peer_txcredit() - manually update stream peer tx credit
+ *
+ * \param wsi: the h2 child stream whose peer credit to change
+ * \param sid: the stream ID, or LWS_H2_STREAM_SID for the wsi stream ID
+ * \param bump: signed change to confer upon peer tx credit for sid
+ *
+ * In conjunction with LCCSCF_H2_MANUAL_RXFLOW flag, allows the user code to
+ * selectively starve the remote peer of the ability to send us data on a client
+ * connection.
+ *
+ * Normally lws sends an initial window size for the peer to send to it of 0,
+ * but during the header phase it sends a WINDOW_UPDATE to increase the amount
+ * available. LCCSCF_H2_MANUAL_RXFLOW restricts this initial increase in tx
+ * credit for the stream, before it has been asked to send us anything, to the
+ * amount specified in the client info .manual_initial_tx_credit member, and
+ * this api can be called to send the other side permission to send us up to
+ * \p bump additional bytes.
+ *
+ * The nwsi tx credit is updated automatically for exactly what was sent to us
+ * on a stream with LCCSCF_H2_MANUAL_RXFLOW flag, but the stream's own tx credit
+ * must be handled manually by user code via this api.
+ *
+ * Returns 0 for success or nonzero for failure.
+ */
+#define LWS_H2_STREAM_SID -1
+LWS_VISIBLE LWS_EXTERN int
+lws_h2_update_peer_txcredit(struct lws *wsi, int sid, int bump);
+
+
+/**
+ * lws_h2_get_peer_txcredit_estimate() - return peer tx credit estimate
+ *
+ * \param wsi: the h2 child stream whose peer credit estimate to return
+ *
+ * Returns the estimated amount of tx credit at the peer, in other words the
+ * number of bytes the peer is authorized to send to us.
+ *
+ * It's an 'estimate' because we don't know how much is already in flight
+ * towards us and actually already used.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_h2_get_peer_txcredit_estimate(struct lws *wsi);
+
+///@}
+
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+enum lws_jws_jose_hdr_indexes {
+ LJJHI_ALG, /* REQUIRED */
+ LJJHI_JKU, /* Optional: string */
+ LJJHI_JWK, /* Optional: jwk JSON object: public key: */
+ LJJHI_KID, /* Optional: string */
+ LJJHI_X5U, /* Optional: string: url of public key cert / chain */
+ LJJHI_X5C, /* Optional: base64 (NOT -url): actual cert */
+ LJJHI_X5T, /* Optional: base64url: SHA-1 of actual cert */
+ LJJHI_X5T_S256, /* Optional: base64url: SHA-256 of actual cert */
+ LJJHI_TYP, /* Optional: string: media type */
+ LJJHI_CTY, /* Optional: string: content media type */
+ LJJHI_CRIT, /* Optional for send, REQUIRED: array of strings:
+ * mustn't contain standardized strings or null set */
+
+ LJJHI_RECIPS_HDR,
+ LJJHI_RECIPS_HDR_ALG,
+ LJJHI_RECIPS_HDR_KID,
+ LJJHI_RECIPS_EKEY,
+
+ LJJHI_ENC, /* JWE only: Optional: string */
+ LJJHI_ZIP, /* JWE only: Optional: string ("DEF" = deflate) */
+
+ LJJHI_EPK, /* Additional arg for JWE ECDH: ephemeral public key */
+ LJJHI_APU, /* Additional arg for JWE ECDH: base64url */
+ LJJHI_APV, /* Additional arg for JWE ECDH: base64url */
+ LJJHI_IV, /* Additional arg for JWE AES: base64url */
+ LJJHI_TAG, /* Additional arg for JWE AES: base64url */
+ LJJHI_P2S, /* Additional arg for JWE PBES2: base64url: salt */
+ LJJHI_P2C, /* Additional arg for JWE PBES2: integer: count */
+
+ LWS_COUNT_JOSE_HDR_ELEMENTS
+};
+
+enum lws_jose_algtype {
+ LWS_JOSE_ENCTYPE_NONE,
+
+ LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5,
+ LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP,
+ LWS_JOSE_ENCTYPE_RSASSA_PKCS1_PSS,
+
+ LWS_JOSE_ENCTYPE_ECDSA,
+ LWS_JOSE_ENCTYPE_ECDHES,
+
+ LWS_JOSE_ENCTYPE_AES_CBC,
+ LWS_JOSE_ENCTYPE_AES_CFB128,
+ LWS_JOSE_ENCTYPE_AES_CFB8,
+ LWS_JOSE_ENCTYPE_AES_CTR,
+ LWS_JOSE_ENCTYPE_AES_ECB,
+ LWS_JOSE_ENCTYPE_AES_OFB,
+ LWS_JOSE_ENCTYPE_AES_XTS, /* care: requires double-length key */
+ LWS_JOSE_ENCTYPE_AES_GCM,
+};
+
+/* there's a table of these defined in lws-gencrypto-common.c */
+
+struct lws_jose_jwe_alg {
+ enum lws_genhash_types hash_type;
+ enum lws_genhmac_types hmac_type;
+ enum lws_jose_algtype algtype_signing; /* the signing cipher */
+ enum lws_jose_algtype algtype_crypto; /* the encryption cipher */
+ const char *alg; /* the JWA enc alg name, eg "ES512" */
+ const char *curve_name; /* NULL, or, eg, "P-256" */
+ unsigned short keybits_min, keybits_fixed;
+ unsigned short ivbits;
+};
+
+/*
+ * For JWS, "JOSE header" is defined to be the union of...
+ *
+ * o JWS Protected Header
+ * o JWS Unprotected Header
+ *
+ * For JWE, the "JOSE header" is the union of...
+ *
+ * o JWE Protected Header
+ * o JWE Shared Unprotected Header
+ * o JWE Per-Recipient Unprotected Header
+ */
+
+#define LWS_JWS_MAX_RECIPIENTS 3
+
+struct lws_jws_recpient {
+ /*
+ * JOSE per-recipient unprotected header... for JWS this contains
+ * protected / header / signature
+ */
+ struct lws_gencrypto_keyelem unprot[LWS_COUNT_JOSE_HDR_ELEMENTS];
+ struct lws_jwk jwk_ephemeral; /* recipient ephemeral key if any */
+ struct lws_jwk jwk; /* recipient "jwk" key if any */
+};
+
+struct lws_jose {
+ /* JOSE protected and unprotected header elements */
+ struct lws_gencrypto_keyelem e[LWS_COUNT_JOSE_HDR_ELEMENTS];
+
+ struct lws_jws_recpient recipient[LWS_JWS_MAX_RECIPIENTS];
+
+ char typ[32];
+
+ /* information from the protected header part */
+ const struct lws_jose_jwe_alg *alg;
+ const struct lws_jose_jwe_alg *enc_alg;
+
+ int recipients; /* count of used recipient[] entries */
+};
+
+/**
+ * lws_jose_init() - prepare a struct lws_jose for use
+ *
+ * \param jose: the jose header struct to prepare
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_jose_init(struct lws_jose *jose);
+
+/**
+ * lws_jose_destroy() - retire a struct lws_jose from use
+ *
+ * \param jose: the jose header struct to destroy
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_jose_destroy(struct lws_jose *jose);
+
+/**
+ * lws_gencrypto_jws_alg_to_definition() - look up a jws alg name
+ *
+ * \param alg: the jws alg name
+ * \param jose: pointer to the pointer to the info struct to set on success
+ *
+ * Returns 0 if *jose set, else nonzero for failure
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_gencrypto_jws_alg_to_definition(const char *alg,
+ const struct lws_jose_jwe_alg **jose);
+
+/**
+ * lws_gencrypto_jwe_alg_to_definition() - look up a jwe alg name
+ *
+ * \param alg: the jwe alg name
+ * \param jose: pointer to the pointer to the info struct to set on success
+ *
+ * Returns 0 if *jose set, else nonzero for failure
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_gencrypto_jwe_alg_to_definition(const char *alg,
+ const struct lws_jose_jwe_alg **jose);
+
+/**
+ * lws_gencrypto_jwe_enc_to_definition() - look up a jwe enc name
+ *
+ * \param alg: the jwe enc name
+ * \param jose: pointer to the pointer to the info struct to set on success
+ *
+ * Returns 0 if *jose set, else nonzero for failure
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_gencrypto_jwe_enc_to_definition(const char *enc,
+ const struct lws_jose_jwe_alg **jose);
+
+/**
+ * lws_jws_parse_jose() - parse a JWS JOSE header
+ *
+ * \param jose: the jose struct to set to parsing results
+ * \param buf: the raw JOSE header
+ * \param len: the length of the raw JOSE header
+ * \param temp: parent-owned buffer to "allocate" elements into
+ * \param temp_len: amount of space available in temp
+ *
+ * returns the amount of temp used, or -1 for error
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_parse_jose(struct lws_jose *jose,
+ const char *buf, int len, char *temp, int *temp_len);
+
+/**
+ * lws_jwe_parse_jose() - parse a JWE JOSE header
+ *
+ * \param jose: the jose struct to set to parsing results
+ * \param buf: the raw JOSE header
+ * \param len: the length of the raw JOSE header
+ * \param temp: parent-owned buffer to "allocate" elements into
+ * \param temp_len: amount of space available in temp
+ *
+ * returns the amount of temp used, or -1 for error
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jwe_parse_jose(struct lws_jose *jose,
+ const char *buf, int len, char *temp, int *temp_len);
+
--- /dev/null
+ /*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * JWE Compact Serialization consists of
+ *
+ * BASE64URL(UTF8(JWE Protected Header)) || '.' ||
+ * BASE64URL(JWE Encrypted Key) || '.' ||
+ * BASE64URL(JWE Initialization Vector) || '.' ||
+ * BASE64URL(JWE Ciphertext) || '.' ||
+ * BASE64URL(JWE Authentication Tag)
+ */
+
+#define LWS_JWE_RFC3394_OVERHEAD_BYTES 8
+#define LWS_JWE_AES_IV_BYTES 16
+
+#define LWS_JWE_LIMIT_RSA_KEY_BITS 4096
+#define LWS_JWE_LIMIT_AES_KEY_BITS (512 + 64) /* RFC3394 Key Wrap adds 64b */
+#define LWS_JWE_LIMIT_EC_KEY_BITS 528 /* 521 rounded to byte boundary */
+#define LWS_JWE_LIMIT_HASH_BITS (LWS_GENHASH_LARGEST * 8)
+
+/* the largest key element for any cipher */
+#define LWS_JWE_LIMIT_KEY_ELEMENT_BYTES (LWS_JWE_LIMIT_RSA_KEY_BITS / 8)
+
+
+struct lws_jwe {
+ struct lws_jose jose;
+ struct lws_jws jws;
+ struct lws_jwk jwk;
+
+ /*
+ * We have to keep a copy of the CEK so we can reuse it with later
+ * key encryptions for the multiple recipient case.
+ */
+ uint8_t cek[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES];
+ unsigned int cek_valid:1;
+
+ int recip;
+};
+
+LWS_VISIBLE LWS_EXTERN void
+lws_jwe_init(struct lws_jwe *jwe, struct lws_context *context);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_jwe_destroy(struct lws_jwe *jwe);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_jwe_be64(uint64_t c, uint8_t *p8);
+
+/*
+ * JWE Compact Serialization consists of
+ *
+ * BASE64URL(UTF8(JWE Protected Header)) || '.' ||
+ * BASE64URL(JWE Encrypted Key) || '.' ||
+ * BASE64URL(JWE Initialization Vector) || '.' ||
+ * BASE64URL(JWE Ciphertext) || '.' ||
+ * BASE64URL(JWE Authentication Tag)
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_jwe_render_compact(struct lws_jwe *jwe, char *out, size_t out_len);
+
+LWS_VISIBLE int
+lws_jwe_render_flattened(struct lws_jwe *jwe, char *out, size_t out_len);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_jwe_json_parse(struct lws_jwe *jwe, const uint8_t *buf, int len,
+ char *temp, int *temp_len);
+
+/**
+ * lws_jwe_auth_and_decrypt() - confirm and decrypt JWE
+ *
+ * \param jose: jose context
+ * \param jws: jws / jwe context... .map and .map_b64 must be filled already
+ *
+ * This is a high level JWE decrypt api that takes a jws with the maps
+ * already processed, and if the authentication passes, returns the decrypted
+ * plaintext in jws.map.buf[LJWE_CTXT] and its length in jws.map.len[LJWE_CTXT].
+ *
+ * In the jws, the following fields must have been set by the caller
+ *
+ * .context
+ * .jwk (the key encryption key)
+ * .map
+ * .map_b64
+ *
+ * Having the b64 and decoded maps filled externally makes it flexible where
+ * the data was picked from, eg, from a Complete JWE JSON serialization, a
+ * flattened one, or a Compact Serialization.
+ *
+ * Returns decrypt length, or -1 for failure.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jwe_auth_and_decrypt(struct lws_jwe *jwe, char *temp, int *temp_len);
+
+/**
+ * lws_jwe_encrypt() - perform JWE encryption
+ *
+ * \param jose: the JOSE header information (encryption types, etc)
+ * \param jws: the JWE elements, pointer to jwk etc
+ * \param temp: parent-owned buffer to "allocate" elements into
+ * \param temp_len: amount of space available in temp
+ *
+ * May be called up to LWS_JWS_MAX_RECIPIENTS times to encrypt the same CEK
+ * multiple ways on the same JWE payload.
+ *
+ * returns the amount of temp used, or -1 for error.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jwe_encrypt(struct lws_jwe *jwe, char *temp, int *temp_len);
+
+/**
+ * lws_jwe_create_packet() - add b64 sig to b64 hdr + payload
+ *
+ * \param jwe: the struct lws_jwe we are trying to render
+ * \param payload: unencoded payload JSON
+ * \param len: length of unencoded payload JSON
+ * \param nonce: Nonse string to include in protected header
+ * \param out: buffer to take signed packet
+ * \param out_len: size of \p out buffer
+ * \param conext: lws_context to get random from
+ *
+ * This creates a "flattened" JWS packet from the jwk and the plaintext
+ * payload, and signs it. The packet is written into \p out.
+ *
+ * This does the whole packet assembly and signing, calling through to
+ * lws_jws_sign_from_b64() as part of the process.
+ *
+ * Returns the length written to \p out, or -1.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jwe_create_packet(struct lws_jwe *jwe,
+ const char *payload, size_t len, const char *nonce,
+ char *out, size_t out_len, struct lws_context *context);
+
+
+/* only exposed because we have test vectors that need it */
+LWS_VISIBLE LWS_EXTERN int
+lws_jwe_auth_and_decrypt_cbc_hs(struct lws_jwe *jwe, uint8_t *enc_cek,
+ uint8_t *aad, int aad_len);
+
+/* only exposed because we have test vectors that need it */
+LWS_VISIBLE LWS_EXTERN int
+lws_jwa_concat_kdf(struct lws_jwe *jwe, int direct,
+ uint8_t *out, const uint8_t *shared_secret, int sslen);
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*! \defgroup jwk JSON Web Keys
+ * ## JSON Web Keys API
+ *
+ * Lws provides an API to parse JSON Web Keys into a struct lws_gencrypto_keyelem.
+ *
+ * "oct" and "RSA" type keys are supported. For "oct" keys, they are held in
+ * the "e" member of the struct lws_gencrypto_keyelem.
+ *
+ * Keys elements are allocated on the heap. You must destroy the allocations
+ * in the struct lws_gencrypto_keyelem by calling
+ * lws_genrsa_destroy_elements() when you are finished with it.
+ */
+///@{
+
+enum enum_jwk_meta_tok {
+ JWK_META_KTY,
+ JWK_META_KID,
+ JWK_META_USE,
+ JWK_META_KEY_OPS,
+ JWK_META_X5C,
+ JWK_META_ALG,
+
+ LWS_COUNT_JWK_ELEMENTS
+};
+
+struct lws_jwk {
+ /* key data elements */
+ struct lws_gencrypto_keyelem e[LWS_GENCRYPTO_MAX_KEYEL_COUNT];
+ /* generic meta key elements, like KID */
+ struct lws_gencrypto_keyelem meta[LWS_COUNT_JWK_ELEMENTS];
+ int kty; /**< one of LWS_JWK_ */
+ char private_key; /* nonzero = has private key elements */
+};
+
+typedef int (*lws_jwk_key_import_callback)(struct lws_jwk *s, void *user);
+
+struct lws_jwk_parse_state {
+ struct lws_jwk *jwk;
+ char b64[(((8192 / 8) * 4) / 3) + 1]; /* enough for 8Kb key */
+ lws_jwk_key_import_callback per_key_cb;
+ void *user;
+ int pos;
+ unsigned short possible;
+};
+
+/** lws_jwk_import() - Create a JSON Web key from the textual representation
+ *
+ * \param jwk: the JWK object to create
+ * \param cb: callback for each jwk-processed key, or NULL if importing a single
+ * key with no parent "keys" JSON
+ * \param user: pointer to be passed to the callback, otherwise ignored by lws.
+ * NULL if importing a single key with no parent "keys" JSON
+ * \param in: a single JWK JSON stanza in utf-8
+ * \param len: the length of the JWK JSON stanza in bytes
+ *
+ * Creates an lws_jwk struct filled with data from the JSON representation.
+ *
+ * There are two ways to use this... with some protocols a single jwk is
+ * delivered with no parent "keys": [] array. If you call this with cb and
+ * user as NULL, then the input will be interpreted like that and the results
+ * placed in s.
+ *
+ * The second case is that you are dealing with a "keys":[] array with one or
+ * more keys in it. In this case, the function iterates through the keys using
+ * s as a temporary jwk, and calls the user-provided callback for each key in
+ * turn while it return 0 (nonzero return from the callback terminates the
+ * iteration through any further keys).
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jwk_import(struct lws_jwk *jwk, lws_jwk_key_import_callback cb, void *user,
+ const char *in, size_t len);
+
+/** lws_jwk_destroy() - Destroy a JSON Web key
+ *
+ * \param jwk: the JWK object to destroy
+ *
+ * All allocations in the lws_jwk are destroyed
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_jwk_destroy(struct lws_jwk *jwk);
+
+/** lws_jwk_dup_oct() - Set a jwk to a dup'd binary OCT key
+ *
+ * \param jwk: the JWK object to set
+ * \param key: the JWK object to destroy
+ * \param len: the JWK object to destroy
+ *
+ * Sets the kty to OCT, allocates len bytes for K and copies len bytes of key
+ * into the allocation.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jwk_dup_oct(struct lws_jwk *jwk, const void *key, int len);
+
+#define LWSJWKF_EXPORT_PRIVATE (1 << 0)
+#define LWSJWKF_EXPORT_NOCRLF (1 << 1)
+
+/** lws_jwk_export() - Export a JSON Web key to a textual representation
+ *
+ * \param jwk: the JWK object to export
+ * \param flags: control export options
+ * \param p: the buffer to write the exported JWK to
+ * \param len: the length of the buffer \p p in bytes... reduced by used amount
+ *
+ * Returns length of the used part of the buffer if OK, or -1 for error.
+ *
+ * \p flags can be OR-ed together
+ *
+ * LWSJWKF_EXPORT_PRIVATE: default is only public part, set this to also export
+ * the private part
+ *
+ * LWSJWKF_EXPORT_NOCRLF: normally adds a CRLF at the end of the export, if
+ * you need to suppress it, set this flag
+ *
+ * Serializes the content of the JWK into a char buffer.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jwk_export(struct lws_jwk *jwk, int flags, char *p, int *len);
+
+/** lws_jwk_load() - Import a JSON Web key from a file
+ *
+ * \param jwk: the JWK object to load into
+ * \param filename: filename to load from
+ * \param cb: optional callback for each key
+ * \param user: opaque user pointer passed to cb if given
+ *
+ * Returns 0 for OK or -1 for failure
+ *
+ * There are two ways to use this... with some protocols a single jwk is
+ * delivered with no parent "keys": [] array. If you call this with cb and
+ * user as NULL, then the input will be interpreted like that and the results
+ * placed in s.
+ *
+ * The second case is that you are dealing with a "keys":[] array with one or
+ * more keys in it. In this case, the function iterates through the keys using
+ * s as a temporary jwk, and calls the user-provided callback for each key in
+ * turn while it return 0 (nonzero return from the callback terminates the
+ * iteration through any further keys, leaving the last one in s).
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jwk_load(struct lws_jwk *jwk, const char *filename,
+ lws_jwk_key_import_callback cb, void *user);
+
+/** lws_jwk_save() - Export a JSON Web key to a file
+ *
+ * \param jwk: the JWK object to save from
+ * \param filename: filename to save to
+ *
+ * Returns 0 for OK or -1 for failure
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jwk_save(struct lws_jwk *jwk, const char *filename);
+
+/** lws_jwk_rfc7638_fingerprint() - jwk to RFC7638 compliant fingerprint
+ *
+ * \param jwk: the JWK object to fingerprint
+ * \param digest32: buffer to take 32-byte digest
+ *
+ * Returns 0 for OK or -1 for failure
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jwk_rfc7638_fingerprint(struct lws_jwk *jwk, char *digest32);
+
+/** lws_jwk_strdup_meta() - allocate a duplicated string meta element
+ *
+ * \param jwk: the JWK object to fingerprint
+ * \param idx: JWK_META_ element index
+ * \param in: string to copy
+ * \param len: length of string to copy
+ *
+ * Returns 0 for OK or -1 for failure
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jwk_strdup_meta(struct lws_jwk *jwk, enum enum_jwk_meta_tok idx,
+ const char *in, int len);
+
+
+LWS_VISIBLE LWS_EXTERN int
+lws_jwk_dump(struct lws_jwk *jwk);
+
+/** lws_jwk_generate() - create a new key of given type and characteristics
+ *
+ * \param context: the struct lws_context used for RNG
+ * \param jwk: the JWK object to fingerprint
+ * \param kty: One of the LWS_GENCRYPTO_KTY_ key types
+ * \param bits: for OCT and RSA keys, the number of bits
+ * \param curve: for EC keys, the name of the curve
+ *
+ * Returns 0 for OK or -1 for failure
+ */
+LWS_VISIBLE int
+lws_jwk_generate(struct lws_context *context, struct lws_jwk *jwk,
+ enum lws_gencrypto_kty kty, int bits, const char *curve);
+
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*! \defgroup jws JSON Web Signature
+ * ## JSON Web Signature API
+ *
+ * Lws provides an API to check and create RFC7515 JSON Web Signatures
+ *
+ * SHA256/384/512 HMAC, and RSA 256/384/512 are supported.
+ *
+ * The API uses your TLS library crypto, but works exactly the same no matter
+ * what your TLS backend is.
+ */
+///@{
+
+/*
+ * The maps are built to work with both JWS (LJWS_) and JWE (LJWE_), and are
+ * sized to the slightly larger JWE case.
+ */
+
+enum enum_jws_sig_elements {
+
+ /* JWS block namespace */
+ LJWS_JOSE,
+ LJWS_PYLD,
+ LJWS_SIG,
+ LJWS_UHDR,
+
+ /* JWE block namespace */
+ LJWE_JOSE = 0,
+ LJWE_EKEY,
+ LJWE_IV,
+ LJWE_CTXT,
+ LJWE_ATAG,
+ LJWE_AAD,
+
+ LWS_JWS_MAX_COMPACT_BLOCKS
+};
+
+struct lws_jws_map {
+ const char *buf[LWS_JWS_MAX_COMPACT_BLOCKS];
+ uint32_t len[LWS_JWS_MAX_COMPACT_BLOCKS];
+};
+
+#define LWS_JWS_MAX_SIGS 3
+
+struct lws_jws {
+ struct lws_jwk *jwk; /* the struct lws_jwk containing the signing key */
+ struct lws_context *context; /* the lws context (used to get random) */
+ struct lws_jws_map map, map_b64;
+};
+
+/* jws EC signatures do not have ASN.1 in them, meaning they're incompatible
+ * with generic signatures.
+ */
+
+/**
+ * lws_jws_init() - initialize a jws for use
+ *
+ * \param jws: pointer to the jws to initialize
+ * \param jwk: the jwk to use with this jws
+ * \param context: the lws_context to use
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_jws_init(struct lws_jws *jws, struct lws_jwk *jwk,
+ struct lws_context *context);
+
+/**
+ * lws_jws_destroy() - scrub a jws
+ *
+ * \param jws: pointer to the jws to destroy
+ *
+ * Call before the jws goes out of scope.
+ *
+ * Elements defined in the jws are zeroed.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_jws_destroy(struct lws_jws *jws);
+
+/**
+ * lws_jws_sig_confirm_compact() - check signature
+ *
+ * \param map: pointers and lengths for each of the unencoded JWS elements
+ * \param jwk: public key
+ * \param context: lws_context
+ * \param temp: scratchpad
+ * \param temp_len: length of scratchpad
+ *
+ * Confirms the signature on a JWS. Use if you have non-b64 plain JWS elements
+ * in a map... it'll make a temp b64 version needed for comparison. See below
+ * for other variants.
+ *
+ * Returns 0 on match.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_sig_confirm_compact(struct lws_jws_map *map, struct lws_jwk *jwk,
+ struct lws_context *context,
+ char *temp, int *temp_len);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_sig_confirm_compact_b64_map(struct lws_jws_map *map_b64,
+ struct lws_jwk *jwk,
+ struct lws_context *context,
+ char *temp, int *temp_len);
+
+/**
+ * lws_jws_sig_confirm_compact_b64() - check signature on b64 compact JWS
+ *
+ * \param in: pointer to b64 jose.payload[.hdr].sig
+ * \param len: bytes available at \p in
+ * \param map: map to take decoded non-b64 content
+ * \param jwk: public key
+ * \param context: lws_context
+ * \param temp: scratchpad
+ * \param temp_len: size of scratchpad
+ *
+ * Confirms the signature on a JWS. Use if you have you have b64 compact layout
+ * (jose.payload.hdr.sig) as an aggregated string... it'll make a temp plain
+ * version needed for comparison.
+ *
+ * Returns 0 on match.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_sig_confirm_compact_b64(const char *in, size_t len,
+ struct lws_jws_map *map,
+ struct lws_jwk *jwk,
+ struct lws_context *context,
+ char *temp, int *temp_len);
+
+/**
+ * lws_jws_sig_confirm() - check signature on plain + b64 JWS elements
+ *
+ * \param map_b64: pointers and lengths for each of the b64-encoded JWS elements
+ * \param map: pointers and lengths for each of the unencoded JWS elements
+ * \param jwk: public key
+ * \param context: lws_context
+ *
+ * Confirms the signature on a JWS. Use if you have you already have both b64
+ * compact layout (jose.payload.hdr.sig) and decoded JWS elements in maps.
+ *
+ * If you had the b64 string and called lws_jws_compact_decode() on it, you
+ * will end up with both maps, and can use this api version, saving needlessly
+ * regenerating any temp map.
+ *
+ * Returns 0 on match.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_sig_confirm(struct lws_jws_map *map_b64, /* b64-encoded */
+ struct lws_jws_map *map, /* non-b64 */
+ struct lws_jwk *jwk, struct lws_context *context);
+
+/**
+ * lws_jws_sign_from_b64() - add b64 sig to b64 hdr + payload
+ *
+ * \param jose: jose header information
+ * \param jws: information to include in the signature
+ * \param b64_sig: output buffer for b64 signature
+ * \param sig_len: size of \p b64_sig output buffer
+ *
+ * This adds a b64-coded JWS signature of the b64-encoded protected header
+ * and b64-encoded payload, at \p b64_sig. The signature will be as large
+ * as the N element of the RSA key when the RSA key is used, eg, 512 bytes for
+ * a 4096-bit key, and then b64-encoding on top.
+ *
+ * In some special cases, there is only payload to sign and no header, in that
+ * case \p b64_hdr may be NULL, and only the payload will be hashed before
+ * signing.
+ *
+ * Returns the length of the encoded signature written to \p b64_sig, or -1.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_sign_from_b64(struct lws_jose *jose, struct lws_jws *jws, char *b64_sig,
+ size_t sig_len);
+
+/**
+ * lws_jws_compact_decode() - converts and maps compact serialization b64 sections
+ *
+ * \param in: the incoming compact serialized b64
+ * \param len: the length of the incoming compact serialized b64
+ * \param map: pointer to the results structure
+ * \param map_b64: NULL, or pointer to a second results structure taking block
+ * information about the undecoded b64
+ * \param out: buffer to hold decoded results
+ * \param out_len: size of out in bytes
+ *
+ * Returns number of sections (2 if "none", else 3), or -1 if illegal.
+ *
+ * map is set to point to the start and hold the length of each decoded block.
+ * If map_b64 is non-NULL, then it's set with information about the input b64
+ * blocks.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_compact_decode(const char *in, int len, struct lws_jws_map *map,
+ struct lws_jws_map *map_b64, char *out, int *out_len);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_compact_encode(struct lws_jws_map *map_b64, /* b64-encoded */
+ const struct lws_jws_map *map, /* non-b64 */
+ char *buf, int *out_len);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_sig_confirm_json(const char *in, size_t len,
+ struct lws_jws *jws, struct lws_jwk *jwk,
+ struct lws_context *context,
+ char *temp, int *temp_len);
+
+/**
+ * lws_jws_write_flattened_json() - create flattened JSON sig
+ *
+ * \param jws: information to include in the signature
+ * \param flattened: output buffer for JSON
+ * \param len: size of \p flattened output buffer
+ *
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_write_flattened_json(struct lws_jws *jws, char *flattened, size_t len);
+
+/**
+ * lws_jws_write_compact() - create flattened JSON sig
+ *
+ * \param jws: information to include in the signature
+ * \param compact: output buffer for compact format
+ * \param len: size of \p flattened output buffer
+ *
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_write_compact(struct lws_jws *jws, char *compact, size_t len);
+
+
+
+/*
+ * below apis are not normally needed if dealing with whole JWS... they're
+ * useful for creating from scratch
+ */
+
+
+/**
+ * lws_jws_dup_element() - allocate space for an element and copy data into it
+ *
+ * \param map: map to create the element in
+ * \param idx: index of element in the map to create
+ * \param temp: space to allocate in
+ * \param temp_len: available space at temp
+ * \param in: data to duplicate into element
+ * \param in_len: length of data to duplicate
+ * \param actual_alloc: 0 for same as in_len, else actual allocation size
+ *
+ * Copies in_len from in to temp, if temp_len is sufficient.
+ *
+ * Returns 0 or -1 if not enough space in temp / temp_len.
+ *
+ * Over-allocation can be acheived by setting actual_alloc to the real
+ * allocation desired... in_len will be copied into it.
+ *
+ * *temp_len is reduced by actual_alloc if successful.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_dup_element(struct lws_jws_map *map, int idx,
+ char *temp, int *temp_len, const void *in, size_t in_len,
+ size_t actual_alloc);
+
+/**
+ * lws_jws_randomize_element() - create an element and fill with random
+ *
+ * \param context: lws_context used for random
+ * \param map: map to create the element in
+ * \param idx: index of element in the map to create
+ * \param temp: space to allocate in
+ * \param temp_len: available space at temp
+ * \param random_len: length of data to fill with random
+ * \param actual_alloc: 0 for same as random_len, else actual allocation size
+ *
+ * Randomize random_len bytes at temp, if temp_len is sufficient.
+ *
+ * Returns 0 or -1 if not enough space in temp / temp_len.
+ *
+ * Over-allocation can be acheived by setting actual_alloc to the real
+ * allocation desired... the first random_len will be filled with random.
+ *
+ * *temp_len is reduced by actual_alloc if successful.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_randomize_element(struct lws_context *context,
+ struct lws_jws_map *map,
+ int idx, char *temp, int *temp_len, size_t random_len,
+ size_t actual_alloc);
+
+/**
+ * lws_jws_alloc_element() - create an element and reserve space for content
+ *
+ * \param map: map to create the element in
+ * \param idx: index of element in the map to create
+ * \param temp: space to allocate in
+ * \param temp_len: available space at temp
+ * \param len: logical length of element
+ * \param actual_alloc: 0 for same as len, else actual allocation size
+ *
+ * Allocate len bytes at temp, if temp_len is sufficient.
+ *
+ * Returns 0 or -1 if not enough space in temp / temp_len.
+ *
+ * Over-allocation can be acheived by setting actual_alloc to the real
+ * allocation desired... the element logical length will be set to len.
+ *
+ * *temp_len is reduced by actual_alloc if successful.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_alloc_element(struct lws_jws_map *map, int idx, char *temp,
+ int *temp_len, size_t len, size_t actual_alloc);
+
+/**
+ * lws_jws_encode_b64_element() - create an b64-encoded element
+ *
+ * \param map: map to create the element in
+ * \param idx: index of element in the map to create
+ * \param temp: space to allocate in
+ * \param temp_len: available space at temp
+ * \param in: pointer to unencoded input
+ * \param in_len: length of unencoded input
+ *
+ * Allocate len bytes at temp, if temp_len is sufficient.
+ *
+ * Returns 0 or -1 if not enough space in temp / temp_len.
+ *
+ * Over-allocation can be acheived by setting actual_alloc to the real
+ * allocation desired... the element logical length will be set to len.
+ *
+ * *temp_len is reduced by actual_alloc if successful.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_encode_b64_element(struct lws_jws_map *map, int idx,
+ char *temp, int *temp_len, const void *in,
+ size_t in_len);
+
+
+/**
+ * lws_jws_b64_compact_map() - find block starts and lengths in compact b64
+ *
+ * \param in: pointer to b64 jose.payload[.hdr].sig
+ * \param len: bytes available at \p in
+ * \param map: output struct with pointers and lengths for each JWS element
+ *
+ * Scans a jose.payload[.hdr].sig b64 string and notes where the blocks start
+ * and their length into \p map.
+ *
+ * Returns number of blocks if OK. May return <0 if malformed.
+ * May not fill all map entries.
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_b64_compact_map(const char *in, int len, struct lws_jws_map *map);
+
+
+/**
+ * lws_jws_base64_enc() - encode input data into b64url data
+ *
+ * \param in: the incoming plaintext
+ * \param in_len: the length of the incoming plaintext in bytes
+ * \param out: the buffer to store the b64url encoded data to
+ * \param out_max: the length of \p out in bytes
+ *
+ * Returns either -1 if problems, or the number of bytes written to \p out.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_base64_enc(const char *in, size_t in_len, char *out, size_t out_max);
+
+/**
+ * lws_jws_encode_section() - encode input data into b64url data,
+ * prepending . if not first
+ *
+ * \param in: the incoming plaintext
+ * \param in_len: the length of the incoming plaintext in bytes
+ * \param first: nonzero if the first section
+ * \param p: the buffer to store the b64url encoded data to
+ * \param end: just past the end of p
+ *
+ * Returns either -1 if problems, or the number of bytes written to \p out.
+ * If the section is not the first one, '.' is prepended.
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_jws_encode_section(const char *in, size_t in_len, int first, char **p,
+ char *end);
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/** \defgroup lejp JSON parser
+ * ##JSON parsing related functions
+ * \ingroup lwsapi
+ *
+ * LEJP is an extremely lightweight JSON stream parser included in lws.
+ */
+//@{
+struct lejp_ctx;
+
+#if !defined(LWS_ARRAY_SIZE)
+#define LWS_ARRAY_SIZE(_x) (sizeof(_x) / sizeof(_x[0]))
+#endif
+#define LEJP_FLAG_WS_KEEP 64
+#define LEJP_FLAG_WS_COMMENTLINE 32
+
+enum lejp_states {
+ LEJP_IDLE = 0,
+ LEJP_MEMBERS = 1,
+ LEJP_M_P = 2,
+ LEJP_MP_STRING = LEJP_FLAG_WS_KEEP | 3,
+ LEJP_MP_STRING_ESC = LEJP_FLAG_WS_KEEP | 4,
+ LEJP_MP_STRING_ESC_U1 = LEJP_FLAG_WS_KEEP | 5,
+ LEJP_MP_STRING_ESC_U2 = LEJP_FLAG_WS_KEEP | 6,
+ LEJP_MP_STRING_ESC_U3 = LEJP_FLAG_WS_KEEP | 7,
+ LEJP_MP_STRING_ESC_U4 = LEJP_FLAG_WS_KEEP | 8,
+ LEJP_MP_DELIM = 9,
+ LEJP_MP_VALUE = 10,
+ LEJP_MP_VALUE_NUM_INT = LEJP_FLAG_WS_KEEP | 11,
+ LEJP_MP_VALUE_NUM_EXP = LEJP_FLAG_WS_KEEP | 12,
+ LEJP_MP_VALUE_TOK = LEJP_FLAG_WS_KEEP | 13,
+ LEJP_MP_COMMA_OR_END = 14,
+ LEJP_MP_ARRAY_END = 15,
+};
+
+enum lejp_reasons {
+ LEJP_CONTINUE = -1,
+ LEJP_REJECT_IDLE_NO_BRACE = -2,
+ LEJP_REJECT_MEMBERS_NO_CLOSE = -3,
+ LEJP_REJECT_MP_NO_OPEN_QUOTE = -4,
+ LEJP_REJECT_MP_STRING_UNDERRUN = -5,
+ LEJP_REJECT_MP_ILLEGAL_CTRL = -6,
+ LEJP_REJECT_MP_STRING_ESC_ILLEGAL_ESC = -7,
+ LEJP_REJECT_ILLEGAL_HEX = -8,
+ LEJP_REJECT_MP_DELIM_MISSING_COLON = -9,
+ LEJP_REJECT_MP_DELIM_BAD_VALUE_START = -10,
+ LEJP_REJECT_MP_VAL_NUM_INT_NO_FRAC = -11,
+ LEJP_REJECT_MP_VAL_NUM_FORMAT = -12,
+ LEJP_REJECT_MP_VAL_NUM_EXP_BAD_EXP = -13,
+ LEJP_REJECT_MP_VAL_TOK_UNKNOWN = -14,
+ LEJP_REJECT_MP_C_OR_E_UNDERF = -15,
+ LEJP_REJECT_MP_C_OR_E_NOTARRAY = -16,
+ LEJP_REJECT_MP_ARRAY_END_MISSING = -17,
+ LEJP_REJECT_STACK_OVERFLOW = -18,
+ LEJP_REJECT_MP_DELIM_ISTACK = -19,
+ LEJP_REJECT_NUM_TOO_LONG = -20,
+ LEJP_REJECT_MP_C_OR_E_NEITHER = -21,
+ LEJP_REJECT_UNKNOWN = -22,
+ LEJP_REJECT_CALLBACK = -23
+};
+
+#define LEJP_FLAG_CB_IS_VALUE 64
+
+enum lejp_callbacks {
+ LEJPCB_CONSTRUCTED = 0,
+ LEJPCB_DESTRUCTED = 1,
+
+ LEJPCB_START = 2,
+ LEJPCB_COMPLETE = 3,
+ LEJPCB_FAILED = 4,
+
+ LEJPCB_PAIR_NAME = 5,
+
+ LEJPCB_VAL_TRUE = LEJP_FLAG_CB_IS_VALUE | 6,
+ LEJPCB_VAL_FALSE = LEJP_FLAG_CB_IS_VALUE | 7,
+ LEJPCB_VAL_NULL = LEJP_FLAG_CB_IS_VALUE | 8,
+ LEJPCB_VAL_NUM_INT = LEJP_FLAG_CB_IS_VALUE | 9,
+ LEJPCB_VAL_NUM_FLOAT = LEJP_FLAG_CB_IS_VALUE | 10,
+ LEJPCB_VAL_STR_START = 11, /* notice handle separately */
+ LEJPCB_VAL_STR_CHUNK = LEJP_FLAG_CB_IS_VALUE | 12,
+ LEJPCB_VAL_STR_END = LEJP_FLAG_CB_IS_VALUE | 13,
+
+ LEJPCB_ARRAY_START = 14,
+ LEJPCB_ARRAY_END = 15,
+
+ LEJPCB_OBJECT_START = 16,
+ LEJPCB_OBJECT_END = 17,
+};
+
+/**
+ * _lejp_callback() - User parser actions
+ * \param ctx: LEJP context
+ * \param reason: Callback reason
+ *
+ * Your user callback is associated with the context at construction time,
+ * and receives calls as the parsing progresses.
+ *
+ * All of the callbacks may be ignored and just return 0.
+ *
+ * The reasons it might get called, found in @reason, are:
+ *
+ * LEJPCB_CONSTRUCTED: The context was just constructed... you might want to
+ * perform one-time allocation for the life of the context.
+ *
+ * LEJPCB_DESTRUCTED: The context is being destructed... if you made any
+ * allocations at construction-time, you can free them now
+ *
+ * LEJPCB_START: Parsing is beginning at the first byte of input
+ *
+ * LEJPCB_COMPLETE: Parsing has completed successfully. You'll get a 0 or
+ * positive return code from lejp_parse indicating the
+ * amount of unused bytes left in the input buffer
+ *
+ * LEJPCB_FAILED: Parsing failed. You'll get a negative error code
+ * returned from lejp_parse
+ *
+ * LEJPCB_PAIR_NAME: When a "name":"value" pair has had the name parsed,
+ * this callback occurs. You can find the new name at
+ * the end of ctx->path[]
+ *
+ * LEJPCB_VAL_TRUE: The "true" value appeared
+ *
+ * LEJPCB_VAL_FALSE: The "false" value appeared
+ *
+ * LEJPCB_VAL_NULL: The "null" value appeared
+ *
+ * LEJPCB_VAL_NUM_INT: A string representing an integer is in ctx->buf
+ *
+ * LEJPCB_VAL_NUM_FLOAT: A string representing a float is in ctx->buf
+ *
+ * LEJPCB_VAL_STR_START: We are starting to parse a string, no data yet
+ *
+ * LEJPCB_VAL_STR_CHUNK: We parsed LEJP_STRING_CHUNK -1 bytes of string data in
+ * ctx->buf, which is as much as we can buffer, so we are
+ * spilling it. If all your strings are less than
+ * LEJP_STRING_CHUNK - 1 bytes, you will never see this
+ * callback.
+ *
+ * LEJPCB_VAL_STR_END: String parsing has completed, the last chunk of the
+ * string is in ctx->buf.
+ *
+ * LEJPCB_ARRAY_START: An array started
+ *
+ * LEJPCB_ARRAY_END: An array ended
+ *
+ * LEJPCB_OBJECT_START: An object started
+ *
+ * LEJPCB_OBJECT_END: An object ended
+ */
+LWS_EXTERN signed char _lejp_callback(struct lejp_ctx *ctx, char reason);
+
+typedef signed char (*lejp_callback)(struct lejp_ctx *ctx, char reason);
+
+#ifndef LEJP_MAX_PARSING_STACK_DEPTH
+#define LEJP_MAX_PARSING_STACK_DEPTH 5
+#endif
+#ifndef LEJP_MAX_DEPTH
+#define LEJP_MAX_DEPTH 12
+#endif
+#ifndef LEJP_MAX_INDEX_DEPTH
+#define LEJP_MAX_INDEX_DEPTH 5
+#endif
+#ifndef LEJP_MAX_PATH
+#define LEJP_MAX_PATH 128
+#endif
+#ifndef LEJP_STRING_CHUNK
+/* must be >= 30 to assemble floats */
+#define LEJP_STRING_CHUNK 254
+#endif
+
+enum num_flags {
+ LEJP_SEEN_MINUS = (1 << 0),
+ LEJP_SEEN_POINT = (1 << 1),
+ LEJP_SEEN_POST_POINT = (1 << 2),
+ LEJP_SEEN_EXP = (1 << 3)
+};
+
+struct _lejp_stack {
+ char s; /* lejp_state stack*/
+ char p; /* path length */
+ char i; /* index array length */
+ char b; /* user bitfield */
+};
+
+struct _lejp_parsing_stack {
+ void *user; /* private to the stack level */
+ signed char (*callback)(struct lejp_ctx *ctx, char reason);
+ const char * const *paths;
+ uint8_t count_paths;
+ uint8_t ppos;
+ uint8_t path_match;
+};
+
+struct lejp_ctx {
+
+ /* sorted by type for most compact alignment
+ *
+ * pointers
+ */
+ void *user;
+
+ /* arrays */
+
+ struct _lejp_parsing_stack pst[LEJP_MAX_PARSING_STACK_DEPTH];
+ struct _lejp_stack st[LEJP_MAX_DEPTH];
+ uint16_t i[LEJP_MAX_INDEX_DEPTH]; /* index array */
+ uint16_t wild[LEJP_MAX_INDEX_DEPTH]; /* index array */
+ char path[LEJP_MAX_PATH];
+ char buf[LEJP_STRING_CHUNK + 1];
+
+ /* size_t */
+
+ size_t path_stride; /* 0 means default ptr size, else stride */
+
+ /* int */
+
+ uint32_t line;
+
+ /* short */
+
+ uint16_t uni;
+
+ /* char */
+
+ uint8_t npos;
+ uint8_t dcount;
+ uint8_t f;
+ uint8_t sp; /* stack head */
+ uint8_t ipos; /* index stack depth */
+ uint8_t count_paths;
+ uint8_t path_match;
+ uint8_t path_match_len;
+ uint8_t wildcount;
+ uint8_t pst_sp; /* parsing stack head */
+};
+
+LWS_VISIBLE LWS_EXTERN void
+lejp_construct(struct lejp_ctx *ctx,
+ signed char (*callback)(struct lejp_ctx *ctx, char reason),
+ void *user, const char * const *paths, unsigned char paths_count);
+
+LWS_VISIBLE LWS_EXTERN void
+lejp_destruct(struct lejp_ctx *ctx);
+
+LWS_VISIBLE LWS_EXTERN int
+lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len);
+
+LWS_VISIBLE LWS_EXTERN void
+lejp_change_callback(struct lejp_ctx *ctx,
+ signed char (*callback)(struct lejp_ctx *ctx, char reason));
+
+/*
+ * push the current paths / paths_count and lejp_cb to a stack in the ctx, and
+ * start using the new ones
+ */
+LWS_VISIBLE LWS_EXTERN int
+lejp_parser_push(struct lejp_ctx *ctx, void *user, const char * const *paths,
+ unsigned char paths_count, lejp_callback lejp_cb);
+
+/*
+ * pop the previously used paths / paths_count and lejp_cb, and continue
+ * parsing using those as before
+ */
+LWS_VISIBLE LWS_EXTERN int
+lejp_parser_pop(struct lejp_ctx *ctx);
+
+/* exported for use when reevaluating a path for use with a subcontext */
+LWS_VISIBLE LWS_EXTERN void
+lejp_check_path_match(struct lejp_ctx *ctx);
+
+LWS_VISIBLE LWS_EXTERN int
+lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len);
+
+LWS_VISIBLE LWS_EXTERN const char *
+lejp_error_to_string(int e);
+//@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/** \defgroup log Logging
+ *
+ * ##Logging
+ *
+ * Lws provides flexible and filterable logging facilities, which can be
+ * used inside lws and in user code.
+ *
+ * Log categories may be individually filtered bitwise, and directed to built-in
+ * sinks for syslog-compatible logging, or a user-defined function.
+ */
+///@{
+
+#define LLL_ERR (1 << 0)
+#define LLL_WARN (1 << 1)
+#define LLL_NOTICE (1 << 2)
+#define LLL_INFO (1 << 3)
+#define LLL_DEBUG (1 << 4)
+#define LLL_PARSER (1 << 5)
+#define LLL_HEADER (1 << 6)
+#define LLL_EXT (1 << 7)
+#define LLL_CLIENT (1 << 8)
+#define LLL_LATENCY (1 << 9)
+#define LLL_USER (1 << 10)
+#define LLL_THREAD (1 << 11)
+
+#define LLL_COUNT (12) /* set to count of valid flags */
+
+/**
+ * lwsl_timestamp: generate logging timestamp string
+ *
+ * \param level: logging level
+ * \param p: char * buffer to take timestamp
+ * \param len: length of p
+ *
+ * returns length written in p
+ */
+LWS_VISIBLE LWS_EXTERN int
+lwsl_timestamp(int level, char *p, int len);
+
+#if defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_NETWORK)
+#define _lws_log(aaa, ...) SMSG(__VA_ARGS__)
+#else
+LWS_VISIBLE LWS_EXTERN void _lws_log(int filter, const char *format, ...) LWS_FORMAT(2);
+LWS_VISIBLE LWS_EXTERN void _lws_logv(int filter, const char *format, va_list vl);
+#endif
+
+/*
+ * Figure out which logs to build in or not
+ */
+
+#if defined(_DEBUG)
+ /*
+ * In DEBUG build, select all logs unless NO_LOGS
+ */
+ #if defined(LWS_WITH_NO_LOGS)
+ #define _LWS_LINIT (LLL_ERR | LLL_USER)
+ #else
+ #define _LWS_LINIT ((1 << LLL_COUNT) - 1)
+ #endif
+#else /* not _DEBUG */
+ #define _LWS_LINIT (LLL_ERR | LLL_USER | LLL_WARN | LLL_NOTICE)
+#endif /* _DEBUG */
+
+/*
+ * Create either empty overrides or the ones forced at build-time.
+ * These overrides have the final say... any bits set in
+ * LWS_LOGGING_BITFIELD_SET force the build of those logs, any bits
+ * set in LWS_LOGGING_BITFIELD_CLEAR disable the build of those logs.
+ *
+ * If not defined lws decides based on CMAKE_BUILD_TYPE=DEBUG or not
+ */
+
+#if defined(LWS_LOGGING_BITFIELD_SET)
+ #define _LWS_LBS (LWS_LOGGING_BITFIELD_SET)
+#else
+ #define _LWS_LBS 0
+#endif
+
+#if defined(LWS_LOGGING_BITFIELD_CLEAR)
+ #define _LWS_LBC (LWS_LOGGING_BITFIELD_CLEAR)
+#else
+ #define _LWS_LBC 0
+#endif
+
+/*
+ * Compute the final active logging bitfield for build
+ */
+#define _LWS_ENABLED_LOGS (((_LWS_LINIT) | (_LWS_LBS)) & ~(_LWS_LBC))
+
+/*
+ * Individually enable or disable log levels for build
+ * depending on what was computed
+ */
+
+#if (_LWS_ENABLED_LOGS & LLL_ERR)
+#define lwsl_err(...) _lws_log(LLL_ERR, __VA_ARGS__)
+#else
+#define lwsl_err(...) do {} while(0)
+#endif
+
+#if (_LWS_ENABLED_LOGS & LLL_WARN)
+#define lwsl_warn(...) _lws_log(LLL_WARN, __VA_ARGS__)
+#else
+#define lwsl_warn(...) do {} while(0)
+#endif
+
+#if (_LWS_ENABLED_LOGS & LLL_NOTICE)
+#define lwsl_notice(...) _lws_log(LLL_NOTICE, __VA_ARGS__)
+#else
+#define lwsl_notice(...) do {} while(0)
+#endif
+
+#if (_LWS_ENABLED_LOGS & LLL_INFO)
+#define lwsl_info(...) _lws_log(LLL_INFO, __VA_ARGS__)
+#else
+#define lwsl_info(...) do {} while(0)
+#endif
+
+#if (_LWS_ENABLED_LOGS & LLL_DEBUG)
+#define lwsl_debug(...) _lws_log(LLL_DEBUG, __VA_ARGS__)
+#else
+#define lwsl_debug(...) do {} while(0)
+#endif
+
+#if (_LWS_ENABLED_LOGS & LLL_PARSER)
+#define lwsl_parser(...) _lws_log(LLL_PARSER, __VA_ARGS__)
+#else
+#define lwsl_parser(...) do {} while(0)
+#endif
+
+#if (_LWS_ENABLED_LOGS & LLL_HEADER)
+#define lwsl_header(...) _lws_log(LLL_HEADER, __VA_ARGS__)
+#else
+#define lwsl_header(...) do {} while(0)
+#endif
+
+#if (_LWS_ENABLED_LOGS & LLL_EXT)
+#define lwsl_ext(...) _lws_log(LLL_EXT, __VA_ARGS__)
+#else
+#define lwsl_ext(...) do {} while(0)
+#endif
+
+#if (_LWS_ENABLED_LOGS & LLL_CLIENT)
+#define lwsl_client(...) _lws_log(LLL_CLIENT, __VA_ARGS__)
+#else
+#define lwsl_client(...) do {} while(0)
+#endif
+
+#if (_LWS_ENABLED_LOGS & LLL_LATENCY)
+#define lwsl_latency(...) _lws_log(LLL_LATENCY, __VA_ARGS__)
+#else
+#define lwsl_latency(...) do {} while(0)
+#endif
+
+#if (_LWS_ENABLED_LOGS & LLL_THREAD)
+#define lwsl_thread(...) _lws_log(LLL_THREAD, __VA_ARGS__)
+#else
+#define lwsl_thread(...) do {} while(0)
+#endif
+
+#if (_LWS_ENABLED_LOGS & LLL_USER)
+#define lwsl_user(...) _lws_log(LLL_USER, __VA_ARGS__)
+#else
+#define lwsl_user(...) do {} while(0)
+#endif
+
+
+#define lwsl_hexdump_err(...) lwsl_hexdump_level(LLL_ERR, __VA_ARGS__)
+#define lwsl_hexdump_warn(...) lwsl_hexdump_level(LLL_WARN, __VA_ARGS__)
+#define lwsl_hexdump_notice(...) lwsl_hexdump_level(LLL_NOTICE, __VA_ARGS__)
+#define lwsl_hexdump_info(...) lwsl_hexdump_level(LLL_INFO, __VA_ARGS__)
+#define lwsl_hexdump_debug(...) lwsl_hexdump_level(LLL_DEBUG, __VA_ARGS__)
+
+/**
+ * lwsl_hexdump_level() - helper to hexdump a buffer at a selected debug level
+ *
+ * \param level: one of LLL_ constants
+ * \param vbuf: buffer start to dump
+ * \param len: length of buffer to dump
+ *
+ * If \p level is visible, does a nice hexdump -C style dump of \p vbuf for
+ * \p len bytes. This can be extremely convenient while debugging.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lwsl_hexdump_level(int level, const void *vbuf, size_t len);
+
+/**
+ * lwsl_hexdump() - helper to hexdump a buffer (DEBUG builds only)
+ *
+ * \param buf: buffer start to dump
+ * \param len: length of buffer to dump
+ *
+ * Calls through to lwsl_hexdump_level(LLL_DEBUG, ... for compatability.
+ * It's better to use lwsl_hexdump_level(level, ... directly so you can control
+ * the visibility.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lwsl_hexdump(const void *buf, size_t len);
+
+/**
+ * lws_is_be() - returns nonzero if the platform is Big Endian
+ */
+static LWS_INLINE int lws_is_be(void) {
+ const int probe = ~0xff;
+
+ return *(const char *)&probe;
+}
+
+/**
+ * lws_set_log_level() - Set the logging bitfield
+ * \param level: OR together the LLL_ debug contexts you want output from
+ * \param log_emit_function: NULL to leave it as it is, or a user-supplied
+ * function to perform log string emission instead of
+ * the default stderr one.
+ *
+ * log level defaults to "err", "warn" and "notice" contexts enabled and
+ * emission on stderr. If stderr is a tty (according to isatty()) then
+ * the output is coloured according to the log level using ANSI escapes.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_set_log_level(int level,
+ void (*log_emit_function)(int level, const char *line));
+
+/**
+ * lwsl_emit_syslog() - helper log emit function writes to system log
+ *
+ * \param level: one of LLL_ log level indexes
+ * \param line: log string
+ *
+ * You use this by passing the function pointer to lws_set_log_level(), to set
+ * it as the log emit function, it is not called directly.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lwsl_emit_syslog(int level, const char *line);
+
+/**
+ * lwsl_emit_stderr() - helper log emit function writes to stderr
+ *
+ * \param level: one of LLL_ log level indexes
+ * \param line: log string
+ *
+ * You use this by passing the function pointer to lws_set_log_level(), to set
+ * it as the log emit function, it is not called directly.
+ *
+ * It prepends a system timestamp like [2018/11/13 07:41:57:3989]
+ *
+ * If stderr is a tty, then ansi colour codes are added.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lwsl_emit_stderr(int level, const char *line);
+
+/**
+ * lwsl_emit_stderr_notimestamp() - helper log emit function writes to stderr
+ *
+ * \param level: one of LLL_ log level indexes
+ * \param line: log string
+ *
+ * You use this by passing the function pointer to lws_set_log_level(), to set
+ * it as the log emit function, it is not called directly.
+ *
+ * If stderr is a tty, then ansi colour codes are added.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lwsl_emit_stderr_notimestamp(int level, const char *line);
+
+/**
+ * lwsl_visible() - returns true if the log level should be printed
+ *
+ * \param level: one of LLL_ log level indexes
+ *
+ * This is useful if you have to do work to generate the log content, you
+ * can skip the work if the log level used to print it is not actually
+ * enabled at runtime.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lwsl_visible(int level);
+
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/** \defgroup lwsac lwsac
+ *
+ * ##Allocated Chunks
+ *
+ * If you know you will be allocating a large, unknown number of same or
+ * differently sized objects, it's certainly possible to do it with libc
+ * malloc. However the allocation cost in time and memory overhead can
+ * add up, and deallocation means walking the structure of every object and
+ * freeing them in turn.
+ *
+ * lwsac (LWS Allocated Chunks) allocates chunks intended to be larger
+ * than your objects (4000 bytes by default) which you linearly allocate from
+ * using lwsac_use().
+ *
+ * If your next request won't fit in the current chunk, a new chunk is added
+ * to the chain of chunks and the allocaton done from there. If the request
+ * is larger than the chunk size, an oversize chunk is created to satisfy it.
+ *
+ * When you are finished with the allocations, you call lwsac_free() and
+ * free all the *chunks*. So you may have thousands of objects in the chunks,
+ * but they are all destroyed with the chunks without having to deallocate them
+ * one by one pointlessly.
+ */
+///@{
+
+struct lwsac;
+typedef unsigned char * lwsac_cached_file_t;
+
+
+#define lws_list_ptr_container(P,T,M) ((T *)((char *)(P) - offsetof(T, M)))
+
+/*
+ * linked-list helper that's commonly useful to manage lists of things
+ * allocated using lwsac.
+ *
+ * These lists point to their corresponding "next" member in the target, NOT
+ * the original containing struct. To get the containing struct, you must use
+ * lws_list_ptr_container() to convert.
+ *
+ * It's like that because it means we no longer have to have the next pointer
+ * at the start of the struct, and we can have the same struct on multiple
+ * linked-lists with everything held in the struct itself.
+ */
+typedef void * lws_list_ptr;
+
+/*
+ * optional sorting callback called by lws_list_ptr_insert() to sort the right
+ * things inside the opqaue struct being sorted / inserted on the list.
+ */
+typedef int (*lws_list_ptr_sort_func_t)(lws_list_ptr a, lws_list_ptr b);
+
+#define lws_list_ptr_advance(_lp) _lp = *((void **)_lp)
+
+/* sort may be NULL if you don't care about order */
+LWS_VISIBLE LWS_EXTERN void
+lws_list_ptr_insert(lws_list_ptr *phead, lws_list_ptr *add,
+ lws_list_ptr_sort_func_t sort);
+
+
+/**
+ * lwsac_use - allocate / use some memory from a lwsac
+ *
+ * \param head: pointer to the lwsac list object
+ * \param ensure: the number of bytes we want to use
+ * \param chunk_size: 0, or the size of the chunk to (over)allocate if
+ * what we want won't fit in the current tail chunk. If
+ * 0, the default value of 4000 is used. If ensure is
+ * larger, it is used instead.
+ *
+ * This also serves to init the lwsac if *head is NULL. Basically it does
+ * whatever is necessary to return you a pointer to ensure bytes of memory
+ * reserved for the caller.
+ *
+ * This always allocates in the current chunk or a new chunk... see the
+ * lwsac_use_backfill() variant to try first to find space in earlier chunks.
+ *
+ * Returns NULL if OOM.
+ */
+LWS_VISIBLE LWS_EXTERN void *
+lwsac_use(struct lwsac **head, size_t ensure, size_t chunk_size);
+
+/**
+ * lwsac_use_backfill - allocate / use some memory from a lwsac
+ *
+ * \param head: pointer to the lwsac list object
+ * \param ensure: the number of bytes we want to use
+ * \param chunk_size: 0, or the size of the chunk to (over)allocate if
+ * what we want won't fit in the current tail chunk. If
+ * 0, the default value of 4000 is used. If ensure is
+ * larger, it is used instead.
+ *
+ * This also serves to init the lwsac if *head is NULL. Basically it does
+ * whatever is necessary to return you a pointer to ensure bytes of memory
+ * reserved for the caller.
+ *
+ * Also checks if earlier blocks have enough remaining space to take the
+ * allocation before making a new allocation.
+ *
+ * Returns NULL if OOM.
+ */
+LWS_VISIBLE LWS_EXTERN void *
+lwsac_use_backfill(struct lwsac **head, size_t ensure, size_t chunk_size);
+
+/**
+ * lwsac_use - allocate / use some memory from a lwsac
+ *
+ * \param head: pointer to the lwsac list object
+ * \param ensure: the number of bytes we want to use, which must be zeroed
+ * \param chunk_size: 0, or the size of the chunk to (over)allocate if
+ * what we want won't fit in the current tail chunk. If
+ * 0, the default value of 4000 is used. If ensure is
+ * larger, it is used instead.
+ *
+ * Same as lwsac_use(), but \p ensure bytes of memory at the return address
+ * are zero'd before returning.
+ *
+ * Returns NULL if OOM.
+ */
+LWS_VISIBLE LWS_EXTERN void *
+lwsac_use_zero(struct lwsac **head, size_t ensure, size_t chunk_size);
+
+#define lwsac_use_zeroed lwsac_use_zero
+
+/**
+ * lwsac_free - deallocate all chunks in the lwsac and set head NULL
+ *
+ * \param head: pointer to the lwsac list object
+ *
+ * This deallocates all chunks in the lwsac, then sets *head to NULL. All
+ * lwsac_use() pointers are invalidated in one hit without individual frees.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lwsac_free(struct lwsac **head);
+
+/*
+ * Optional helpers useful for where consumers may need to defer destruction
+ * until all consumers are finished with the lwsac
+ */
+
+/**
+ * lwsac_detach() - destroy an lwsac unless somebody else is referencing it
+ *
+ * \param head: pointer to the lwsac list object
+ *
+ * The creator of the lwsac can all this instead of lwsac_free() when it itself
+ * has finished with the lwsac, but other code may be consuming it.
+ *
+ * If there are no other references, the lwsac is destroyed, *head is set to
+ * NULL and that's the end; however if something else has called
+ * lwsac_reference() on the lwsac, it simply returns. When lws_unreference()
+ * is called and no references are left, it will be destroyed then.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lwsac_detach(struct lwsac **head);
+
+/**
+ * lwsac_reference() - increase the lwsac reference count
+ *
+ * \param head: pointer to the lwsac list object
+ *
+ * Increment the reference count on the lwsac to defer destruction.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lwsac_reference(struct lwsac *head);
+
+/**
+ * lwsac_reference() - increase the lwsac reference count
+ *
+ * \param head: pointer to the lwsac list object
+ *
+ * Decrement the reference count on the lwsac... if it reached 0 on a detached
+ * lwsac then the lwsac is immediately destroyed and *head set to NULL.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lwsac_unreference(struct lwsac **head);
+
+/**
+ * lwsac_extend() - try to increase the size of the last block
+ *
+ * \param head: pointer to the lwsac list object
+ * \param amount: amount to try to increase usage for
+ *
+ * This will either increase the usage reservation of the last allocated block
+ * by amount and return 0, or fail and return 1.
+ *
+ * This is very cheap to call and is designed to optimize usage after a static
+ * struct for vari-sized additional content which may flow into an additional
+ * block in a new chunk if necessary, but wants to make the most of the space
+ * in front of it first to try to avoid gaps and the new chunk if it can.
+ *
+ * The additional area if the call succeeds will have been memset to 0.
+ *
+ * To use it, the following must be true:
+ *
+ * - only the last lwsac use can be extended
+ *
+ * - if another use happens inbetween the use and extend, it will break
+ *
+ * - the use cannot have been using backfill
+ *
+ * - a user object must be tracking the current allocated size of the last use
+ * (lwsac doesn't know it) and increment by amount if the extend call succeeds
+ *
+ * Despite these restrictions this can be an important optimization for some
+ * cases
+ */
+LWS_VISIBLE LWS_EXTERN int
+lwsac_extend(struct lwsac *head, int amount);
+
+/* helpers to keep a file cached in memory */
+
+LWS_VISIBLE LWS_EXTERN void
+lwsac_use_cached_file_start(lwsac_cached_file_t cache);
+
+LWS_VISIBLE LWS_EXTERN void
+lwsac_use_cached_file_end(lwsac_cached_file_t *cache);
+
+LWS_VISIBLE LWS_EXTERN void
+lwsac_use_cached_file_detach(lwsac_cached_file_t *cache);
+
+LWS_VISIBLE LWS_EXTERN int
+lwsac_cached_file(const char *filepath, lwsac_cached_file_t *cache,
+ size_t *len);
+
+/* more advanced helpers */
+
+/* offset from lac to start of payload, first = 1 = first lac in chain */
+LWS_VISIBLE LWS_EXTERN size_t
+lwsac_sizeof(int first);
+
+LWS_VISIBLE LWS_EXTERN size_t
+lwsac_get_tail_pos(struct lwsac *lac);
+
+LWS_VISIBLE LWS_EXTERN struct lwsac *
+lwsac_get_next(struct lwsac *lac);
+
+LWS_VISIBLE LWS_EXTERN size_t
+lwsac_align(size_t length);
+
+LWS_VISIBLE LWS_EXTERN void
+lwsac_info(struct lwsac *head);
+
+LWS_VISIBLE LWS_EXTERN uint64_t
+lwsac_total_alloc(struct lwsac *head);
+
+LWS_VISIBLE LWS_EXTERN uint64_t
+lwsac_total_overhead(struct lwsac *head);
+
+/**
+ * lwsac_scan_extant() - returns existing copy of blob, or NULL
+ *
+ * \param head: the lwsac to scan
+ * \param find: the blob to look for
+ * \param len: the length of the blob to look for
+ * \param nul: nonzero if the next byte must be NUL
+ *
+ * Helper that looks through a whole lwsac for a given binary blob already
+ * present. Used in the case that lwsac contents are const once written, and
+ * strings or blobs may be repeated in the input: this allows the earlier
+ * copy to be pointed to by subsequent references without repeating the string
+ * or blob redundantly.
+ */
+LWS_VISIBLE LWS_EXTERN uint8_t *
+lwsac_scan_extant(struct lwsac *head, uint8_t *find, size_t len, int nul);
+
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#if defined(LWS_WITH_SPAWN)
+
+#if defined(WIN32) || defined(_WIN32)
+#else
+#include <sys/wait.h>
+#include <sys/times.h>
+#endif
+#endif
+
+/** \defgroup misc Miscellaneous APIs
+* ##Miscellaneous APIs
+*
+* Various APIs outside of other categories
+*/
+///@{
+
+struct lws_buflist;
+
+/**
+ * lws_buflist_append_segment(): add buffer to buflist at head
+ *
+ * \param head: list head
+ * \param buf: buffer to stash
+ * \param len: length of buffer to stash
+ *
+ * Returns -1 on OOM, 1 if this was the first segment on the list, and 0 if
+ * it was a subsequent segment.
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_buflist_append_segment(struct lws_buflist **head, const uint8_t *buf,
+ size_t len);
+/**
+ * lws_buflist_next_segment_len(): number of bytes left in current segment
+ *
+ * \param head: list head
+ * \param buf: if non-NULL, *buf is written with the address of the start of
+ * the remaining data in the segment
+ *
+ * Returns the number of bytes left in the current segment. 0 indicates
+ * that the buflist is empty (there are no segments on the buflist).
+ */
+LWS_VISIBLE LWS_EXTERN size_t
+lws_buflist_next_segment_len(struct lws_buflist **head, uint8_t **buf);
+
+/**
+ * lws_buflist_use_segment(): remove len bytes from the current segment
+ *
+ * \param head: list head
+ * \param len: number of bytes to mark as used
+ *
+ * If len is less than the remaining length of the current segment, the position
+ * in the current segment is simply advanced and it returns.
+ *
+ * If len uses up the remaining length of the current segment, then the segment
+ * is deleted and the list head moves to the next segment if any.
+ *
+ * Returns the number of bytes left in the current segment. 0 indicates
+ * that the buflist is empty (there are no segments on the buflist).
+ */
+LWS_VISIBLE LWS_EXTERN size_t
+lws_buflist_use_segment(struct lws_buflist **head, size_t len);
+
+/**
+ * lws_buflist_total_len(): Get the total size of the buflist
+ *
+ * \param head: list head
+ *
+ * Returns the total number of bytes held on all segments of the buflist
+ */
+LWS_VISIBLE LWS_EXTERN size_t
+lws_buflist_total_len(struct lws_buflist **head);
+
+/**
+ * lws_buflist_linear_copy(): copy everything out as one without consuming
+ *
+ * \param head: list head
+ * \param ofs: start offset into buflist in bytes
+ * \param buf: buffer to copy linearly into
+ * \param len: length of buffer available
+ *
+ * Returns -1 if len is too small, or bytes copied. Happy to do partial
+ * copies, returns 0 when there are no more bytes to copy.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_buflist_linear_copy(struct lws_buflist **head, size_t ofs, uint8_t *buf,
+ size_t len);
+
+/**
+ * lws_buflist_destroy_all_segments(): free all segments on the list
+ *
+ * \param head: list head
+ *
+ * This frees everything on the list unconditionally. *head is always
+ * NULL after this.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_buflist_destroy_all_segments(struct lws_buflist **head);
+
+/**
+ * lws_buflist_describe(): debug helper logging buflist status
+ *
+ * \param head: list head
+ * \param id: pointer shown in debug list
+ * \param reason: reason string show in debug list
+ *
+ * Iterates through the buflist segments showing position and size.
+ * This only exists when lws was built in debug mode
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_buflist_describe(struct lws_buflist **head, void *id, const char *reason);
+
+/**
+ * lws_ptr_diff(): helper to report distance between pointers as an int
+ *
+ * \param head: the pointer with the larger address
+ * \param tail: the pointer with the smaller address
+ *
+ * This helper gives you an int representing the number of bytes further
+ * forward the first pointer is compared to the second pointer.
+ */
+#define lws_ptr_diff(head, tail) \
+ ((int)((char *)(head) - (char *)(tail)))
+
+/**
+ * lws_snprintf(): snprintf that truncates the returned length too
+ *
+ * \param str: destination buffer
+ * \param size: bytes left in destination buffer
+ * \param format: format string
+ * \param ...: args for format
+ *
+ * This lets you correctly truncate buffers by concatenating lengths, if you
+ * reach the limit the reported length doesn't exceed the limit.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_snprintf(char *str, size_t size, const char *format, ...) LWS_FORMAT(3);
+
+/**
+ * lws_strncpy(): strncpy that guarantees NUL on truncated copy
+ *
+ * \param dest: destination buffer
+ * \param src: source buffer
+ * \param size: bytes left in destination buffer
+ *
+ * This lets you correctly truncate buffers by concatenating lengths, if you
+ * reach the limit the reported length doesn't exceed the limit.
+ */
+LWS_VISIBLE LWS_EXTERN char *
+lws_strncpy(char *dest, const char *src, size_t size);
+
+/*
+ * Variation where we want to use the smaller of two lengths, useful when the
+ * source string is not NUL terminated
+ */
+#define lws_strnncpy(dest, src, size1, destsize) \
+ lws_strncpy(dest, src, (size_t)(size1 + 1) < (size_t)(destsize) ? \
+ (size_t)(size1 + 1) : (size_t)(destsize))
+
+/**
+ * lws_hex_to_byte_array(): convert hex string like 0123456789ab into byte data
+ *
+ * \param h: incoming NUL-terminated hex string
+ * \param dest: array to fill with binary decodes of hex pairs from h
+ * \param max: maximum number of bytes dest can hold, must be at least half
+ * the size of strlen(h)
+ *
+ * This converts hex strings into an array of 8-bit representations, ie the
+ * input "abcd" produces two bytes of value 0xab and 0xcd.
+ *
+ * Returns number of bytes produced into \p dest, or -1 on error.
+ *
+ * Errors include non-hex chars and an odd count of hex chars in the input
+ * string.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_hex_to_byte_array(const char *h, uint8_t *dest, int max);
+
+/*
+ * lws_timingsafe_bcmp(): constant time memcmp
+ *
+ * \param a: first buffer
+ * \param b: second buffer
+ * \param len: count of bytes to compare
+ *
+ * Return 0 if the two buffers are the same, else nonzero.
+ *
+ * Always compares all of the buffer before returning, so it can't be used as
+ * a timing oracle.
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_timingsafe_bcmp(const void *a, const void *b, uint32_t len);
+
+/**
+ * lws_get_random(): fill a buffer with platform random data
+ *
+ * \param context: the lws context
+ * \param buf: buffer to fill
+ * \param len: how much to fill
+ *
+ * Fills buf with len bytes of random. Returns the number of bytes set, if
+ * not equal to len, then getting the random failed.
+ */
+LWS_VISIBLE LWS_EXTERN size_t
+lws_get_random(struct lws_context *context, void *buf, size_t len);
+/**
+ * lws_daemonize(): make current process run in the background
+ *
+ * \param _lock_path: the filepath to write the lock file
+ *
+ * Spawn lws as a background process, taking care of various things
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_daemonize(const char *_lock_path);
+/**
+ * lws_get_library_version(): return string describing the version of lws
+ *
+ * On unix, also includes the git describe
+ */
+LWS_VISIBLE LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT
+lws_get_library_version(void);
+
+/**
+ * lws_wsi_user() - get the user data associated with the connection
+ * \param wsi: lws connection
+ *
+ * Not normally needed since it's passed into the callback
+ */
+LWS_VISIBLE LWS_EXTERN void *
+lws_wsi_user(struct lws *wsi);
+
+/**
+ * lws_set_wsi_user() - set the user data associated with the client connection
+ * \param wsi: lws connection
+ * \param user: user data
+ *
+ * By default lws allocates this and it's not legal to externally set it
+ * yourself. However client connections may have it set externally when the
+ * connection is created... if so, this api can be used to modify it at
+ * runtime additionally.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_set_wsi_user(struct lws *wsi, void *user);
+
+/**
+ * lws_parse_uri: cut up prot:/ads:port/path into pieces
+ * Notice it does so by dropping '\0' into input string
+ * and the leading / on the path is consequently lost
+ *
+ * \param p: incoming uri string.. will get written to
+ * \param prot: result pointer for protocol part (https://)
+ * \param ads: result pointer for address part
+ * \param port: result pointer for port part
+ * \param path: result pointer for path part
+ *
+ * You may also refer to unix socket addresses, using a '+' at the start of
+ * the address. In this case, the address should end with ':', which is
+ * treated as the separator between the address and path (the normal separator
+ * '/' is a valid part of the socket path). Eg,
+ *
+ * http://+/var/run/mysocket:/my/path
+ *
+ * If the first character after the + is '@', it's interpreted by lws client
+ * processing as meaning to use linux abstract namespace sockets, the @ is
+ * replaced with a '\0' before use.
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_parse_uri(char *p, const char **prot, const char **ads, int *port,
+ const char **path);
+/**
+ * lws_cmdline_option(): simple commandline parser
+ *
+ * \param argc: count of argument strings
+ * \param argv: argument strings
+ * \param val: string to find
+ *
+ * Returns NULL if the string \p val is not found in the arguments.
+ *
+ * If it is found, then it returns a pointer to the next character after \p val.
+ * So if \p val is "-d", then for the commandlines "myapp -d15" and
+ * "myapp -d 15", in both cases the return will point to the "15".
+ *
+ * In the case there is no argument, like "myapp -d", the return will
+ * either point to the '\\0' at the end of -d, or to the start of the
+ * next argument, ie, will be non-NULL.
+ */
+LWS_VISIBLE LWS_EXTERN const char *
+lws_cmdline_option(int argc, const char **argv, const char *val);
+
+/**
+ * lws_cmdline_option_handle_builtin(): apply standard cmdline options
+ *
+ * \param argc: count of argument strings
+ * \param argv: argument strings
+ * \param info: context creation info
+ *
+ * Applies standard options to the context creation info to save them having
+ * to be (unevenly) copied into the minimal examples.
+ *
+ * Applies default log levels that can be overriden by -d
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_cmdline_option_handle_builtin(int argc, const char **argv,
+ struct lws_context_creation_info *info);
+
+/**
+ * lws_now_secs(): return seconds since 1970-1-1
+ */
+LWS_VISIBLE LWS_EXTERN unsigned long
+lws_now_secs(void);
+
+/**
+ * lws_now_usecs(): return useconds since 1970-1-1
+ */
+LWS_VISIBLE LWS_EXTERN lws_usec_t
+lws_now_usecs(void);
+
+/**
+ * lws_get_context - Allow getting lws_context from a Websocket connection
+ * instance
+ *
+ * With this function, users can access context in the callback function.
+ * Otherwise users may have to declare context as a global variable.
+ *
+ * \param wsi: Websocket connection instance
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_context * LWS_WARN_UNUSED_RESULT
+lws_get_context(const struct lws *wsi);
+
+/**
+ * lws_get_vhost_listen_port - Find out the port number a vhost is listening on
+ *
+ * In the case you passed 0 for the port number at context creation time, you
+ * can discover the port number that was actually chosen for the vhost using
+ * this api.
+ *
+ * \param vhost: Vhost to get listen port from
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_get_vhost_listen_port(struct lws_vhost *vhost);
+
+/**
+ * lws_get_count_threads(): how many service threads the context uses
+ *
+ * \param context: the lws context
+ *
+ * By default this is always 1, if you asked for more than lws can handle it
+ * will clip the number of threads. So you can use this to find out how many
+ * threads are actually in use.
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_get_count_threads(struct lws_context *context);
+
+/**
+ * lws_get_parent() - get parent wsi or NULL
+ * \param wsi: lws connection
+ *
+ * Specialized wsi like cgi stdin/out/err are associated to a parent wsi,
+ * this allows you to get their parent.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
+lws_get_parent(const struct lws *wsi);
+
+/**
+ * lws_get_child() - get child wsi or NULL
+ * \param wsi: lws connection
+ *
+ * Allows you to find a related wsi from the parent wsi.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
+lws_get_child(const struct lws *wsi);
+
+/**
+ * lws_get_effective_uid_gid() - find out eventual uid and gid while still root
+ *
+ * \param context: lws context
+ * \param uid: pointer to uid result
+ * \param gid: pointer to gid result
+ *
+ * This helper allows you to find out what the uid and gid for the process will
+ * be set to after the privileges are dropped, beforehand. So while still root,
+ * eg in LWS_CALLBACK_PROTOCOL_INIT, you can arrange things like cache dir
+ * and subdir creation / permissions down /var/cache dynamically.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_get_effective_uid_gid(struct lws_context *context, int *uid, int *gid);
+
+/**
+ * lws_get_udp() - get wsi's udp struct
+ *
+ * \param wsi: lws connection
+ *
+ * Returns NULL or pointer to the wsi's UDP-specific information
+ */
+LWS_VISIBLE LWS_EXTERN const struct lws_udp * LWS_WARN_UNUSED_RESULT
+lws_get_udp(const struct lws *wsi);
+
+LWS_VISIBLE LWS_EXTERN void *
+lws_get_opaque_parent_data(const struct lws *wsi);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_set_opaque_parent_data(struct lws *wsi, void *data);
+
+LWS_VISIBLE LWS_EXTERN void *
+lws_get_opaque_user_data(const struct lws *wsi);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_set_opaque_user_data(struct lws *wsi, void *data);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_get_child_pending_on_writable(const struct lws *wsi);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_clear_child_pending_on_writable(struct lws *wsi);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_get_close_length(struct lws *wsi);
+
+LWS_VISIBLE LWS_EXTERN unsigned char *
+lws_get_close_payload(struct lws *wsi);
+
+/**
+ * lws_get_network_wsi() - Returns wsi that has the tcp connection for this wsi
+ *
+ * \param wsi: wsi you have
+ *
+ * Returns wsi that has the tcp connection (which may be the incoming wsi)
+ *
+ * HTTP/1 connections will always return the incoming wsi
+ * HTTP/2 connections may return a different wsi that has the tcp connection
+ */
+LWS_VISIBLE LWS_EXTERN
+struct lws *lws_get_network_wsi(struct lws *wsi);
+
+/**
+ * lws_set_allocator() - custom allocator support
+ *
+ * \param realloc
+ *
+ * Allows you to replace the allocator (and deallocator) used by lws
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_set_allocator(void *(*realloc)(void *ptr, size_t size, const char *reason));
+
+enum {
+ /*
+ * Flags for enable and disable rxflow with reason bitmap and with
+ * backwards-compatible single bool
+ */
+ LWS_RXFLOW_REASON_USER_BOOL = (1 << 0),
+ LWS_RXFLOW_REASON_HTTP_RXBUFFER = (1 << 6),
+ LWS_RXFLOW_REASON_H2_PPS_PENDING = (1 << 7),
+
+ LWS_RXFLOW_REASON_APPLIES = (1 << 14),
+ LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT = (1 << 13),
+ LWS_RXFLOW_REASON_APPLIES_ENABLE = LWS_RXFLOW_REASON_APPLIES |
+ LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT,
+ LWS_RXFLOW_REASON_APPLIES_DISABLE = LWS_RXFLOW_REASON_APPLIES,
+ LWS_RXFLOW_REASON_FLAG_PROCESS_NOW = (1 << 12),
+
+};
+
+/**
+ * lws_rx_flow_control() - Enable and disable socket servicing for
+ * received packets.
+ *
+ * If the output side of a server process becomes choked, this allows flow
+ * control for the input side.
+ *
+ * \param wsi: Websocket connection instance to get callback for
+ * \param enable: 0 = disable read servicing for this connection, 1 = enable
+ *
+ * If you need more than one additive reason for rxflow control, you can give
+ * iLWS_RXFLOW_REASON_APPLIES_ENABLE or _DISABLE together with one or more of
+ * b5..b0 set to idicate which bits to enable or disable. If any bits are
+ * enabled, rx on the connection is suppressed.
+ *
+ * LWS_RXFLOW_REASON_FLAG_PROCESS_NOW flag may also be given to force any change
+ * in rxflowbstatus to benapplied immediately, this should be used when you are
+ * changing a wsi flow control state from outside a callback on that wsi.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_rx_flow_control(struct lws *wsi, int enable);
+
+/**
+ * lws_rx_flow_allow_all_protocol() - Allow all connections with this protocol to receive
+ *
+ * When the user server code realizes it can accept more input, it can
+ * call this to have the RX flow restriction removed from all connections using
+ * the given protocol.
+ * \param context: lws_context
+ * \param protocol: all connections using this protocol will be allowed to receive
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_rx_flow_allow_all_protocol(const struct lws_context *context,
+ const struct lws_protocols *protocol);
+
+/**
+ * lws_remaining_packet_payload() - Bytes to come before "overall"
+ * rx fragment is complete
+ * \param wsi: Websocket instance (available from user callback)
+ *
+ * This tracks how many bytes are left in the current ws fragment, according
+ * to the ws length given in the fragment header.
+ *
+ * If the message was in a single fragment, and there is no compression, this
+ * is the same as "how much data is left to read for this message".
+ *
+ * However, if the message is being sent in multiple fragments, this will
+ * reflect the unread amount of the current **fragment**, not the message. With
+ * ws, it is legal to not know the length of the message before it completes.
+ *
+ * Additionally if the message is sent via the negotiated permessage-deflate
+ * extension, this number only tells the amount of **compressed** data left to
+ * be read, since that is the only information available at the ws layer.
+ */
+LWS_VISIBLE LWS_EXTERN size_t
+lws_remaining_packet_payload(struct lws *wsi);
+
+#if defined(LWS_WITH_DIR)
+
+typedef enum {
+ LDOT_UNKNOWN,
+ LDOT_FILE,
+ LDOT_DIR,
+ LDOT_LINK,
+ LDOT_FIFO,
+ LDOTT_SOCKET,
+ LDOT_CHAR,
+ LDOT_BLOCK
+} lws_dir_obj_type_t;
+
+struct lws_dir_entry {
+ const char *name;
+ lws_dir_obj_type_t type;
+};
+
+typedef int
+lws_dir_callback_function(const char *dirpath, void *user,
+ struct lws_dir_entry *lde);
+
+/**
+ * lws_dir() - get a callback for everything in a directory
+ *
+ * \param dirpath: the directory to scan
+ * \param user: pointer to give to callback
+ * \param cb: callback to receive information on each file or dir
+ *
+ * Calls \p cb (with \p user) for every object in dirpath.
+ *
+ * This wraps whether it's using POSIX apis, or libuv (as needed for windows,
+ * since it refuses to support POSIX apis for this).
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_dir(const char *dirpath, void *user, lws_dir_callback_function cb);
+#endif
+
+/**
+ * lws_get_allocated_heap() - if the platform supports it, returns amount of
+ * heap allocated by lws itself
+ *
+ * On glibc currently, this reports the total amount of current logical heap
+ * allocation, found by tracking the amount allocated by lws_malloc() and
+ * friends and accounting for freed allocations via lws_free().
+ *
+ * This is useful for confirming where processwide heap allocations actually
+ * come from... this number represents all lws internal allocations, for
+ * fd tables, wsi allocations, ah, etc combined. It doesn't include allocations
+ * from user code, since lws_malloc() etc are not exported from the library.
+ *
+ * On other platforms, it always returns 0.
+ */
+size_t lws_get_allocated_heap(void);
+
+/**
+ * lws_get_tsi() - Get thread service index wsi belong to
+ * \param wsi: websocket connection to check
+ *
+ * Returns more than zero (or zero if only one service thread as is the default).
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_get_tsi(struct lws *wsi);
+
+/**
+ * lws_is_ssl() - Find out if connection is using SSL
+ * \param wsi: websocket connection to check
+ *
+ * Returns nonzero if the wsi is inside a tls tunnel, else zero.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_is_ssl(struct lws *wsi);
+/**
+ * lws_is_cgi() - find out if this wsi is running a cgi process
+ *
+ * \param wsi: lws connection
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_is_cgi(struct lws *wsi);
+
+/**
+ * lws_open() - platform-specific wrapper for open that prepares the fd
+ *
+ * \param __file: the filepath to open
+ * \param __oflag: option flags
+ *
+ * This is a wrapper around platform open() that sets options on the fd
+ * according to lws policy. Currently that is FD_CLOEXEC to stop the opened
+ * fd being available to any child process forked by user code.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_open(const char *__file, int __oflag, ...);
+
+struct lws_wifi_scan { /* generic wlan scan item */
+ struct lws_wifi_scan *next;
+ char ssid[32];
+ int32_t rssi; /* divide by .count to get db */
+ uint8_t bssid[6];
+ uint8_t count;
+ uint8_t channel;
+ uint8_t authmode;
+};
+
+#if defined(LWS_WITH_TLS) && !defined(LWS_WITH_MBEDTLS)
+/**
+ * lws_get_ssl() - Return wsi's SSL context structure
+ * \param wsi: websocket connection
+ *
+ * Returns pointer to the SSL library's context structure
+ */
+LWS_VISIBLE LWS_EXTERN SSL*
+lws_get_ssl(struct lws *wsi);
+#endif
+
+LWS_VISIBLE LWS_EXTERN void
+lws_explicit_bzero(void *p, size_t len);
+
+typedef struct lws_humanize_unit {
+ const char *name; /* array ends with NULL name */
+ uint64_t factor;
+} lws_humanize_unit_t;
+
+LWS_VISIBLE LWS_EXTERN const lws_humanize_unit_t humanize_schema_si[7];
+LWS_VISIBLE LWS_EXTERN const lws_humanize_unit_t humanize_schema_si_bytes[7];
+LWS_VISIBLE LWS_EXTERN const lws_humanize_unit_t humanize_schema_us[8];
+
+/**
+ * lws_humanize() - Convert possibly large number to human-readable uints
+ *
+ * \param buf: result string buffer
+ * \param len: remaining length in \p buf
+ * \param value: the uint64_t value to represent
+ * \param schema: and array of scaling factors and units
+ *
+ * This produces a concise string representation of \p value, referencing the
+ * schema \p schema of scaling factors and units to find the smallest way to
+ * render it.
+ *
+ * Three schema are exported from lws for general use, humanize_schema_si, which
+ * represents as, eg, " 22.130Gi" or " 128 "; humanize_schema_si_bytes
+ * which is the same but shows, eg, " 22.130GiB", and humanize_schema_us,
+ * which represents a count of us as a human-readable time like " 14.350min",
+ * or " 1.500d".
+ *
+ * You can produce your own schema.
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_humanize(char *buf, int len, uint64_t value,
+ const lws_humanize_unit_t *schema);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_ser_wu16be(uint8_t *b, uint16_t u);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_ser_wu32be(uint8_t *b, uint32_t u32);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_ser_wu64be(uint8_t *b, uint64_t u64);
+
+LWS_VISIBLE LWS_EXTERN uint16_t
+lws_ser_ru16be(const uint8_t *b);
+
+LWS_VISIBLE LWS_EXTERN uint32_t
+lws_ser_ru32be(const uint8_t *b);
+
+LWS_VISIBLE LWS_EXTERN uint64_t
+lws_ser_ru64be(const uint8_t *b);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_vbi_encode(uint64_t value, void *buf);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_vbi_decode(const void *buf, uint64_t *value, size_t len);
+
+///@}
+
+#if defined(LWS_WITH_SPAWN)
+
+/* opaque internal struct */
+struct lws_spawn_piped;
+
+typedef void (*lsp_cb_t)(void *opaque, lws_usec_t *accounting, siginfo_t *si,
+ int we_killed_him);
+
+
+/**
+ * lws_spawn_piped_info - details given to create a spawned pipe
+ *
+ * \p owner: lws_dll2_owner_t that lists all active spawns, or NULL
+ * \p vh: vhost to bind stdwsi to... from opt_parent if given
+ * \p opt_parent: optional parent wsi for stdwsi
+ * \p exec_array: argv for process to spawn
+ * \p env_array: environment for spawned process, NULL ends env list
+ * \p protocol_name: NULL, or vhost protocol name to bind stdwsi to
+ * \p chroot_path: NULL, or chroot patch for child process
+ * \p wd: working directory to cd to after fork, NULL defaults to /tmp
+ * \p plsp: NULL, or pointer to the outer lsp pointer so it can be set NULL when destroyed
+ * \p opaque: pointer passed to the reap callback, if any
+ * \p timeout: optional us-resolution timeout, or zero
+ * \p reap_cb: callback when child process has been reaped and the lsp destroyed
+ * \p tsi: tsi to bind stdwsi to... from opt_parent if given
+ */
+struct lws_spawn_piped_info {
+ struct lws_dll2_owner *owner;
+ struct lws_vhost *vh;
+ struct lws *opt_parent;
+
+ const char * const *exec_array;
+ char **env_array;
+ const char *protocol_name;
+ const char *chroot_path;
+ const char *wd;
+
+ struct lws_spawn_piped **plsp;
+
+ void *opaque;
+
+ lsp_cb_t reap_cb;
+
+ lws_usec_t timeout_us;
+ int max_log_lines;
+ int tsi;
+
+ const struct lws_role_ops *ops; /* NULL is raw file */
+
+ uint8_t disable_ctrlc;
+};
+
+/**
+ * lws_spawn_piped() - spawn a child process with stdxxx redirected
+ *
+ * \p lspi: info struct describing details of spawn to create
+ *
+ * This spawns a child process managed in the lsp object and with attributes
+ * set in the arguments. The stdin/out/err streams are redirected to pipes
+ * which are instantiated into wsi that become child wsi of \p parent if non-
+ * NULL. .opaque_user_data on the stdwsi created is set to point to the
+ * lsp object, so this can be recovered easily in the protocol handler.
+ *
+ * If \p owner is non-NULL, successful spawns join the given dll2 owner in the
+ * original process.
+ *
+ * If \p timeout is non-zero, successful spawns register a sul with the us-
+ * resolution timeout to callback \p timeout_cb, in the original process.
+ *
+ * Returns 0 if the spawn went OK or nonzero if it failed and was cleaned up.
+ * The spawned process continues asynchronously and this will return after
+ * starting it if all went well.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_spawn_piped *
+lws_spawn_piped(const struct lws_spawn_piped_info *lspi);
+
+/*
+ * lws_spawn_piped_kill_child_process() - attempt to kill child process
+ *
+ * \p lsp: child object to kill
+ *
+ * Attempts to signal the child process in \p lsp to terminate.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_spawn_piped_kill_child_process(struct lws_spawn_piped *lsp);
+
+/**
+ * lws_spawn_stdwsi_closed() - inform the spawn one of its stdxxx pipes closed
+ *
+ * \p lsp: the spawn object
+ *
+ * When you notice one of the spawn stdxxx pipes closed, inform the spawn
+ * instance using this api. When it sees all three have closed, it will
+ * automatically try to reap the child process.
+ *
+ * This is the mechanism whereby the spawn object can understand its child
+ * has closed.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_spawn_stdwsi_closed(struct lws_spawn_piped *lsp);
+
+/**
+ * lws_spawn_get_stdfd() - return std channel index for stdwsi
+ *
+ * \p wsi: the wsi
+ *
+ * If you know wsi is a stdwsi from a spawn, you can determine its original
+ * channel index / fd before the pipes replaced the default fds. It will return
+ * one of 0 (STDIN), 1 (STDOUT) or 2 (STDERR). You can handle all three in the
+ * same protocol handler and then disambiguate them using this api.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_spawn_get_stdfd(struct lws *wsi);
+
+#endif
+
+struct lws_fsmount {
+ const char *layers_path; /* where layers live */
+ const char *overlay_path; /* where overlay instantiations live */
+
+ char mp[256]; /* mountpoint path */
+ char ovname[64]; /* unique name for mount instance */
+ char distro[64]; /* unique name for layer source */
+
+#if defined(__linux__)
+ const char *layers[4]; /* distro layers, like "base", "env" */
+#endif
+};
+
+/**
+ * lws_fsmount_mount() - Mounts an overlayfs stack of layers
+ *
+ * \p fsm: struct lws_fsmount specifying the mount layout
+ *
+ * This api is able to assemble up to 4 layer directories on to a mountpoint
+ * using overlayfs mount (Linux only).
+ *
+ * Set fsm.layers_path to the base dir where the layers themselves live, the
+ * entries in fsm.layers[] specifies the relative path to the layer, comprising
+ * fsm.layers_path/fsm.distro/fsm.layers[], with [0] being the deepest, earliest
+ * layer and the rest being progressively on top of [0]; NULL indicates the
+ * layer is unused.
+ *
+ * fsm.overlay_path is the base path of the overlayfs instantiations... empty
+ * dirs must exist at
+ *
+ * fsm.overlay_path/overlays/fsm.ovname/work
+ * fsm.overlay_path/overlays/fsm.ovname/session
+ *
+ * Set fsm.mp to the path of an already-existing empty dir that will be the
+ * mountpoint, this can be whereever you like.
+ *
+ * Overlayfs merges the union of all the contributing layers at the mountpoint,
+ * the mount is writeable but the layer themselves are immutable, all additions
+ * and changes are stored in
+ *
+ * fsm.overlay_path/overlays/fsm.ovname/session
+ *
+ * Returns 0 if mounted OK, nonzero if errors.
+ *
+ * Retain fsm for use with unmounting.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_fsmount_mount(struct lws_fsmount *fsm);
+
+/**
+ * lws_fsmount_unmount() - Unmounts an overlayfs dir
+ *
+ * \p fsm: struct lws_fsmount specifying the mount layout
+ *
+ * Unmounts the mountpoint in fsm.mp.
+ *
+ * Delete fsm.overlay_path/overlays/fsm.ovname/session to permanently eradicate
+ * all changes from the time the mountpoint was in use.
+ *
+ * Returns 0 if unmounted OK.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_fsmount_unmount(struct lws_fsmount *fsm);
--- /dev/null
+/*
+ * libwebsockets - protocol - mqtt
+ *
+ * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * included from libwebsockets.h
+ */
+
+#ifndef _LWS_MQTT_H
+#define _LWS_MQTT_H 1
+
+struct _lws_mqtt_related;
+typedef struct _lws_mqtt_related lws_mqtt_related_t;
+struct lws_mqtt_str_st;
+typedef struct lws_mqtt_str_st lws_mqtt_str_t;
+
+#define MQTT_VER_3_1_1 4
+
+#define LWS_MQTT_FINAL_PART 1
+
+typedef enum {
+ QOS0,
+ QOS1,
+ QOS2, /* not supported */
+ RESERVED_QOS_LEVEL,
+ FAILURE_QOS_LEVEL = 0x80
+} lws_mqtt_qos_levels_t;
+
+typedef union {
+ struct {
+ uint8_t retain:1;
+ uint8_t qos:2;
+ uint8_t dup:1;
+ uint8_t ctrl_pkt_type:4;
+ } flags;
+ uint8_t bits;
+} lws_mqtt_fixed_hdr_t;
+
+/*
+ * MQTT connection parameters, passed into struct
+ * lws_client_connect_info to establish a connection using
+ * lws_client_connect_via_info().
+*/
+typedef struct lws_mqtt_client_connect_param_s {
+ const char *client_id; /* Client ID */
+ uint16_t keep_alive; /* MQTT keep alive
+ interval in
+ seconds */
+ uint8_t clean_start; /* MQTT clean
+ session */
+ struct {
+ const char *topic;
+ const char *message;
+ lws_mqtt_qos_levels_t qos;
+ uint8_t retain;
+ } will_param; /* MQTT LWT
+ parameters */
+ const char *username;
+ const char *password;
+} lws_mqtt_client_connect_param_t;
+
+/*
+ * MQTT publish parameters
+*/
+typedef struct lws_mqtt_publish_param_s {
+ char *topic; /* Topic Name */
+ uint16_t topic_len;
+ const void *payload; /* Publish Payload */
+ uint32_t payload_len; /* Size of the
+ complete payload */
+ uint32_t payload_pos; /* where we are in payload */
+ lws_mqtt_qos_levels_t qos;
+
+ /*--v-Following will be used by LWS-v--*/
+ uint16_t packet_id; /* Packet ID for QoS >
+ 0 */
+ uint8_t dup:1; /* Retried PUBLISH,
+ for QoS > 0 */
+} lws_mqtt_publish_param_t;
+
+typedef struct topic_elem {
+ const char *name; /* Topic Name */
+ lws_mqtt_qos_levels_t qos; /* Requested QoS */
+
+ /*--v-Following will be used by LWS-v--*/
+ uint8_t acked;
+} lws_mqtt_topic_elem_t;
+
+/*
+ * MQTT publish parameters
+*/
+typedef struct lws_mqtt_subscribe_param_s {
+ uint32_t num_topics; /* Number of topics */
+ lws_mqtt_topic_elem_t *topic; /* Array of topic elements */
+
+ /*--v-Following will be used by LWS-v--*/
+ uint16_t packet_id;
+} lws_mqtt_subscribe_param_t;
+
+typedef enum {
+ LMQCP_RESERVED,
+ LMQCP_CTOS_CONNECT, /* Connection request */
+ LMQCP_STOC_CONNACK, /* Connection acknowledgment */
+ LMQCP_PUBLISH, /* Publish Message */
+ LMQCP_PUBACK, /* QoS 1: Publish acknowledgment */
+ LMQCP_PUBREC, /* QoS 2.1: Publish received */
+ LMQCP_PUBREL, /* QoS 2.2: Publish release */
+ LMQCP_PUBCOMP, /* QoS 2.3: Publish complete */
+ LMQCP_CTOS_SUBSCRIBE, /* Subscribe request */
+ LMQCP_STOC_SUBACK, /* Subscribe acknowledgment */
+ LMQCP_CTOS_UNSUBSCRIBE, /* Unsubscribe request */
+ LMQCP_STOC_UNSUBACK, /* Unsubscribe acknowledgment */
+ LMQCP_CTOS_PINGREQ, /* PING request */
+ LMQCP_STOC_PINGRESP, /* PONG response */
+ LMQCP_DISCONNECT, /* Disconnect notification */
+ LMQCP_AUTH /* Authentication exchange */
+} lws_mqtt_control_packet_t;
+
+/* flags from byte 8 of C_TO_S CONNECT */
+typedef enum {
+ LMQCFT_USERNAME = (1 << 7),
+ LMQCFT_PASSWORD = (1 << 6),
+ LMQCFT_WILL_RETAIN = (1 << 5),
+ LMQCFT_WILL_QOS = (1 << 3),
+ LMQCFT_WILL_FLAG = (1 << 2),
+ LMQCFT_CLEAN_START = (1 << 1),
+ LMQCFT_RESERVED = (1 << 0),
+
+ LMQCFT_WILL_QOS_MASK = (3 << 3),
+} lws_mqtt_connect_flags_t;
+
+/* flags for S_TO_C CONNACK */
+typedef enum {
+ LMQCFT_SESSION_PRESENT = (1 << 0),
+} lws_mqtt_connack_flags_t;
+
+typedef enum {
+ LMQCP_REASON_SUCCESS = 0x00,
+ LMQCP_REASON_NORMAL_DISCONNECTION = 0x00,
+ LMQCP_REASON_GRANTED_QOS0 = 0x00,
+ LMQCP_REASON_GRANTED_QOS1 = 0x01,
+ LMQCP_REASON_GRANTED_QOS2 = 0x02,
+ LMQCP_REASON_DISCONNECT_WILL = 0x04,
+ LMQCP_REASON_NO_MATCHING_SUBSCRIBER = 0x10,
+ LMQCP_REASON_NO_SUBSCRIPTION_EXISTED = 0x11,
+ LMQCP_REASON_CONTINUE_AUTHENTICATION = 0x18,
+ LMQCP_REASON_RE_AUTHENTICATE = 0x19,
+
+ LMQCP_REASON_UNSPECIFIED_ERROR = 0x80,
+ LMQCP_REASON_MALFORMED_PACKET = 0x81,
+ LMQCP_REASON_PROTOCOL_ERROR = 0x82,
+ LMQCP_REASON_IMPLEMENTATION_SPECIFIC_ERROR = 0x83,
+
+ /* Begin - Error codes for CONNACK */
+ LMQCP_REASON_UNSUPPORTED_PROTOCOL = 0x84,
+ LMQCP_REASON_CLIENT_ID_INVALID = 0x85,
+ LMQCP_REASON_BAD_CREDENTIALS = 0x86,
+ LMQCP_REASON_NOT_AUTHORIZED = 0x87,
+ /* End - Error codes for CONNACK */
+
+ LMQCP_REASON_SERVER_UNAVAILABLE = 0x88,
+ LMQCP_REASON_SERVER_BUSY = 0x89,
+ LMQCP_REASON_BANNED = 0x8a,
+ LMQCP_REASON_SERVER_SHUTTING_DOWN = 0x8b,
+ LMQCP_REASON_BAD_AUTHENTICATION_METHOD = 0x8c,
+ LMQCP_REASON_KEEPALIVE_TIMEOUT = 0x8d,
+ LMQCP_REASON_SESSION_TAKEN_OVER = 0x8e,
+ LMQCP_REASON_TOPIC_FILTER_INVALID = 0x8f,
+ LMQCP_REASON_TOPIC_NAME_INVALID = 0x90,
+ LMQCP_REASON_PACKET_ID_IN_USE = 0x91,
+ LMQCP_REASON_PACKET_ID_NOT_FOUND = 0x92,
+ LMQCP_REASON_MAX_RX_EXCEEDED = 0x93,
+ LMQCP_REASON_TOPIC_ALIAS_INVALID = 0x94,
+ LMQCP_REASON_PACKET_TOO_LARGE = 0x95,
+ LMQCP_REASON_RATELIMIT = 0x96,
+ LMQCP_REASON_QUOTA_EXCEEDED = 0x97,
+ LMQCP_REASON_ADMINISTRATIVE_ACTION = 0x98,
+ LMQCP_REASON_PAYLOAD_FORMAT_INVALID = 0x99,
+ LMQCP_REASON_RETAIN_NOT_SUPPORTED = 0x9a,
+ LMQCP_REASON_QOS_NOT_SUPPORTED = 0x9b,
+ LMQCP_REASON_USE_ANOTHER_SERVER = 0x9c,
+ LMQCP_REASON_SERVER_MOVED = 0x9d,
+ LMQCP_REASON_SHARED_SUBSCRIPTIONS_NOT_SUPPORTED = 0x9e,
+ LMQCP_REASON_CONNECTION_RATE_EXCEEDED = 0x9f,
+ LMQCP_REASON_MAXIMUM_CONNECT_TIME = 0xa0,
+ LMQCP_REASON_SUBSCRIPTION_IDS_NOT_SUPPORTED = 0xa1,
+ LMQCP_REASON_WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED = 0xa2,
+} lws_mqtt_reason_t;
+
+typedef enum {
+ LMQPROP_INVALID,
+ LMQPROP_PAYLOAD_FORMAT_INDICATOR = 0x01,
+ LMQPROP_MESSAGE_EXPIRY_INTERVAL = 0x02,
+ LMQPROP_CONTENT_TYPE = 0x03,
+ LMQPROP_RESPONSE_TOPIC = 0x08,
+ LMQPROP_CORRELATION_DATA = 0x09,
+ LMQPROP_SUBSCRIPTION_IDENTIFIER = 0x0b,
+ LMQPROP_SESSION_EXPIRY_INTERVAL = 0x11,
+ LMQPROP_ASSIGNED_CLIENT_IDENTIFIER = 0x12,
+ LMQPROP_SERVER_KEEP_ALIVE = 0x13,
+ LMQPROP_AUTHENTICATION_METHOD = 0x15,
+ LMQPROP_AUTHENTICATION_DATA = 0x16,
+ LMQPROP_REQUEST_PROBLEM_INFORMATION = 0x17,
+ LMQPROP_WILL_DELAY_INTERVAL = 0x18,
+ LMQPROP_REQUEST_RESPONSE_INFORMATION = 0x19,
+ LMQPROP_RESPONSE_INFORMATION = 0x1a,
+ LMQPROP_SERVER_REFERENCE = 0x1c,
+ LMQPROP_REASON_STRING = 0x1f,
+ LMQPROP_RECEIVE_MAXIMUM = 0x21,
+ LMQPROP_TOPIC_ALIAS_MAXIMUM = 0x22,
+ LMQPROP_TOPIC_ALIAS = 0x23,
+ LMQPROP_MAXIMUM_QOS = 0x24,
+ LMQPROP_RETAIN_AVAILABLE = 0x25,
+ LMQPROP_USER_PROPERTY = 0x26,
+ LMQPROP_MAXIMUM_PACKET_SIZE = 0x27,
+ LMQPROP_WILDCARD_SUBSCRIPTION_AVAIL = 0x28,
+ LMQPROP_SUBSCRIPTION_IDENTIFIER_AVAIL = 0x29,
+ LMQPROP_SHARED_SUBSCRIPTION_AVAIL = 0x2a
+} lws_mqtt_property;
+
+int
+lws_read_mqtt(struct lws *wsi, unsigned char *buf, lws_filepos_t len);
+
+/* returns 0 if bd1 and bd2 are "the same", that includes empty, else nonzero */
+LWS_VISIBLE LWS_EXTERN int
+lws_mqtt_bindata_cmp(const lws_mqtt_str_t *bd1, const lws_mqtt_str_t *bd2);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_mqtt_str_init(lws_mqtt_str_t *s, uint8_t *buf, uint16_t lim, char nf);
+
+LWS_VISIBLE LWS_EXTERN lws_mqtt_str_t *
+lws_mqtt_str_create(uint16_t lim);
+
+LWS_VISIBLE LWS_EXTERN lws_mqtt_str_t *
+lws_mqtt_str_create_init(uint8_t *buf, uint16_t len, uint16_t lim);
+
+LWS_VISIBLE LWS_EXTERN lws_mqtt_str_t *
+lws_mqtt_str_create_cstr_dup(const char *buf, uint16_t lim);
+
+LWS_VISIBLE LWS_EXTERN uint8_t *
+lws_mqtt_str_next(lws_mqtt_str_t *s, uint16_t *budget);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_mqtt_str_advance(lws_mqtt_str_t *s, int n);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_mqtt_str_free(lws_mqtt_str_t **s);
+
+
+/**
+ * lws_mqtt_client_send_publish() - lws_write a publish packet
+ *
+ * \param wsi: the mqtt child wsi
+ * \param pub: additional information on what we're publishing
+ * \param buf: payload to send
+ * \param len: length of data in buf
+ * \param final: flag indicating this is the last part
+ *
+ * Issues part of, or the whole of, a PUBLISH frame. The first part of the
+ * frame contains the header, and uses the .qos and .payload_len parts of \p pub
+ * since MQTT requires the frame to specify the PUBLISH message length at the
+ * start. The \p len paramter may be less than \p pub.payload_len, in which
+ * case subsequent calls with more payload are needed to complete the frame.
+ *
+ * Although the connection is stuck waiting for the remainder, in that it can't
+ * issue any other frames until the current one is completed, lws returns to the
+ * event loop normally and can continue the calls with additional payload even
+ * for huge frames as the data becomes available, consistent with timeout needs
+ * and latency to start any new frame (even, eg, related to ping / pong).
+ *
+ * If you're sending large frames, the OS will typically not allow the data to
+ * be sent all at once to kernel side. So you should ideally cut the payload
+ * up into 1 or 2- mtu sized chunks and send that.
+ *
+ * Final should be set when you're calling with the last part of the payload.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_mqtt_client_send_publish(struct lws *wsi, lws_mqtt_publish_param_t *pub,
+ const void *buf, uint32_t len, int final);
+
+/**
+ * lws_mqtt_client_send_subcribe() - lws_write a subscribe packet
+ *
+ * \param wsi: the mqtt child wsi
+ * \param sub: which topic(s) we want to subscribe to
+ *
+ * For topics other child streams have not already subscribed to, send a packet
+ * to the server asking to subscribe to them. If all topics listed are already
+ * subscribed to be the shared network connection, just trigger the
+ * LWS_CALLBACK_MQTT_SUBSCRIBED callback as if a SUBACK had come.
+ *
+ * \p sub doesn't need to exist after the return from this function.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_mqtt_client_send_subcribe(struct lws *wsi, lws_mqtt_subscribe_param_t *sub);
+
+/**
+ * lws_mqtt_client_send_unsubcribe() - lws_write a unsubscribe packet
+ *
+ * \param wsi: the mqtt child wsi
+ * \param sub: which topic(s) we want to unsubscribe from
+ *
+ * For topics other child streams are not subscribed to, send a packet
+ * to the server asking to unsubscribe from them. If all topics
+ * listed are already subscribed by other child streams on the shared
+ * network connection, just trigger the LWS_CALLBACK_MQTT_UNSUBSCRIBED
+ * callback as if a UNSUBACK had come.
+ *
+ * \p unsub doesn't need to exist after the return from this function.
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_mqtt_client_send_unsubcribe(struct lws *wsi,
+ const lws_mqtt_subscribe_param_t *unsub);
+
+#endif /* _LWS_MQTT_H */
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/** \defgroup net Network related helper APIs
+ * ##Network related helper APIs
+ *
+ * These wrap miscellaneous useful network-related functions
+ */
+///@{
+
+typedef union {
+#if defined(LWS_WITH_IPV6)
+ struct sockaddr_in6 sa6;
+#endif
+ struct sockaddr_in sa4;
+} lws_sockaddr46;
+
+/**
+ * lws_canonical_hostname() - returns this host's hostname
+ *
+ * This is typically used by client code to fill in the host parameter
+ * when making a client connection. You can only call it after the context
+ * has been created.
+ *
+ * \param context: Websocket context
+ */
+LWS_VISIBLE LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT
+lws_canonical_hostname(struct lws_context *context);
+
+/**
+ * lws_get_peer_addresses() - Get client address information
+ * \param wsi: Local struct lws associated with
+ * \param fd: Connection socket descriptor
+ * \param name: Buffer to take client address name
+ * \param name_len: Length of client address name buffer
+ * \param rip: Buffer to take client address IP dotted quad
+ * \param rip_len: Length of client address IP buffer
+ *
+ * This function fills in name and rip with the name and IP of
+ * the client connected with socket descriptor fd. Names may be
+ * truncated if there is not enough room. If either cannot be
+ * determined, they will be returned as valid zero-length strings.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_get_peer_addresses(struct lws *wsi, lws_sockfd_type fd, char *name,
+ int name_len, char *rip, int rip_len);
+
+/**
+ * lws_get_peer_simple() - Get client address information without RDNS
+ *
+ * \param wsi: Local struct lws associated with
+ * \param name: Buffer to take client address name
+ * \param namelen: Length of client address name buffer
+ *
+ * This provides a 123.123.123.123 type IP address in name from the
+ * peer that has connected to wsi
+ */
+LWS_VISIBLE LWS_EXTERN const char *
+lws_get_peer_simple(struct lws *wsi, char *name, size_t namelen);
+
+LWS_VISIBLE LWS_EXTERN const char *
+lws_get_peer_simple_fd(lws_sockfd_type fd, char *name, size_t namelen);
+
+#define LWS_ITOSA_USABLE 0
+#define LWS_ITOSA_NOT_EXIST -1
+#define LWS_ITOSA_NOT_USABLE -2
+#define LWS_ITOSA_BUSY -3 /* only returned by lws_socket_bind() on
+ EADDRINUSE */
+
+#if !defined(LWS_PLAT_FREERTOS) && !defined(LWS_PLAT_OPTEE)
+/**
+ * lws_interface_to_sa() - Convert interface name or IP to sockaddr struct
+ *
+ * \param ipv6: Allow IPV6 addresses
+ * \param ifname: Interface name or IP
+ * \param addr: struct sockaddr_in * to be written
+ * \param addrlen: Length of addr
+ *
+ * This converts a textual network interface name to a sockaddr usable by
+ * other network functions.
+ *
+ * If the network interface doesn't exist, it will return LWS_ITOSA_NOT_EXIST.
+ *
+ * If the network interface is not usable, eg ethernet cable is removed, it
+ * may logically exist but not have any IP address. As such it will return
+ * LWS_ITOSA_NOT_USABLE.
+ *
+ * If the network interface exists and is usable, it will return
+ * LWS_ITOSA_USABLE.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
+ size_t addrlen);
+#endif
+
+/**
+ * lws_sa46_compare_ads() - checks if two sa46 have the same address
+ *
+ * \param sa46a: first
+ * \param sa46b: second
+ *
+ * Returns 0 if the address family and address are the same, otherwise nonzero.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_sa46_compare_ads(const lws_sockaddr46 *sa46a, const lws_sockaddr46 *sa46b);
+
+/*
+ * lws_parse_numeric_address() - converts numeric ipv4 or ipv6 to byte address
+ *
+ * \param ads: the numeric ipv4 or ipv6 address string
+ * \param result: result array
+ * \param max_len: max length of result array
+ *
+ * Converts a 1.2.3.4 or 2001:abcd:123:: or ::ffff:1.2.3.4 formatted numeric
+ * address into an array of network ordered byte address elements.
+ *
+ * Returns < 0 on error, else length of result set, either 4 or 16 for ipv4 /
+ * ipv6.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_parse_numeric_address(const char *ads, uint8_t *result, size_t max_len);
+
+/*
+ * lws_sa46_parse_numeric_address() - converts numeric ipv4 or ipv6 to sa46
+ *
+ * \param ads: the numeric ipv4 or ipv6 address string
+ * \param sa46: pointer to sa46 to set
+ *
+ * Converts a 1.2.3.4 or 2001:abcd:123:: or ::ffff:1.2.3.4 formatted numeric
+ * address into an sa46, a union of sockaddr_in or sockaddr_in6 depending on
+ * what kind of address was found. sa46->sa4.sin_fmaily will be AF_INET if
+ * ipv4, or AF_INET6 if ipv6.
+ *
+ * Returns 0 if the sa46 was set, else < 0 on error.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_sa46_parse_numeric_address(const char *ads, lws_sockaddr46 *sa46);
+
+/**
+ * lws_write_numeric_address() - convert network byte order ads to text
+ *
+ * \param ads: network byte order address array
+ * \param size: number of bytes valid in ads
+ * \param buf: result buffer to take text format
+ * \param len: max size of text buffer
+ *
+ * Converts an array of network-ordered byte address elements to a textual
+ * representation of the numeric address, like "1.2.3.4" or "::1". Return 0
+ * if OK else < 0. ipv6 only supported with LWS_IPV6=1 at cmake.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_write_numeric_address(const uint8_t *ads, int size, char *buf, size_t len);
+
+/**
+ * lws_sa46_write_numeric_address() - convert sa46 ads to textual numeric ads
+ *
+ * \param sa46: the sa46 whose address to show
+ * \param buf: result buffer to take text format
+ * \param len: max size of text buffer
+ *
+ * Converts the ipv4 or ipv6 address in an lws_sockaddr46 to a textual
+ * representation of the numeric address, like "1.2.3.4" or "::1". Return 0
+ * if OK else < 0. ipv6 only supported with LWS_IPV6=1 at cmake.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_sa46_write_numeric_address(lws_sockaddr46 *sa46, char *buf, size_t len);
+
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef __LWS_OPTEE_H
+#define __LWS_OPTEE_H
+
+/* 128-bit IP6 address */
+struct in6_addr {
+ union {
+ uint8_t u6_addr8[16];
+ uint16_t u6_addr16[8];
+ uint32_t u6_addr32[4];
+ };
+};
+
+#define _SS_MAXSIZE 128U
+#define _SS_ALIGNSIZE (sizeof(int64_t))
+#define _SS_PAD1SIZE (_SS_ALIGNSIZE - \
+ sizeof(sa_family_t))
+#define _SS_PAD2SIZE (_SS_MAXSIZE - \
+ sizeof(sa_family_t) - _SS_PAD1SIZE - _SS_ALIGNSIZE)
+
+struct sockaddr_storage {
+ sa_family_t ss_family; /* address family */
+ char __ss_pad1[_SS_PAD1SIZE];
+ int64_t __ss_align; /* force desired struct alignment */
+ char __ss_pad2[_SS_PAD2SIZE];
+};
+
+#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
+struct sockaddr {
+ sa_family_t sa_family; /* address family */
+ uint8_t sa_data[__SOCK_SIZE__ /* address value */
+ - sizeof(sa_family_t)];
+};
+
+/* 16 bytes */
+struct sockaddr_in {
+ sa_family_t sin_family;
+ in_port_t sin_port;
+ struct in_addr sin_addr;
+ uint8_t sin_zero[__SOCK_SIZE__ /* padding until 16 bytes */
+ - sizeof(sa_family_t)
+ - sizeof(in_port_t)
+ - sizeof(struct in_addr)];
+};
+
+struct sockaddr_in6 {
+ sa_family_t sin6_family; /* AF_INET6 */
+ in_port_t sin6_port; /* Transport layer port # */
+ uint32_t sin6_flowinfo; /* IP6 flow information */
+ struct in6_addr sin6_addr; /* IP6 address */
+ uint32_t sin6_scope_id; /* scope zone index */
+};
+
+#endif /* __LWS_OPTEE_H */
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*! \defgroup generic-sessions plugin: generic-sessions
+ * \ingroup Protocols-and-Plugins
+ *
+ * ##Plugin Generic-sessions related
+ *
+ * generic-sessions plugin provides a reusable, generic session and login /
+ * register / forgot password framework including email verification.
+ */
+///@{
+
+#define LWSGS_EMAIL_CONTENT_SIZE 16384
+/**< Maximum size of email we might send */
+
+/* SHA-1 binary and hexified versions */
+/** typedef struct lwsgw_hash_bin */
+typedef struct { unsigned char bin[32]; /**< binary representation of hash */} lwsgw_hash_bin;
+/** typedef struct lwsgw_hash */
+typedef struct { char id[65]; /**< ascii hex representation of hash */ } lwsgw_hash;
+
+/** enum lwsgs_auth_bits */
+enum lwsgs_auth_bits {
+ LWSGS_AUTH_LOGGED_IN = 1, /**< user is logged in as somebody */
+ LWSGS_AUTH_ADMIN = 2, /**< logged in as the admin user */
+ LWSGS_AUTH_VERIFIED = 4, /**< user has verified his email */
+ LWSGS_AUTH_FORGOT_FLOW = 8, /**< just completed "forgot password" */
+};
+
+/** struct lws_session_info - information about user session status */
+struct lws_session_info {
+ char username[32]; /**< username logged in as, or empty string */
+ char email[100]; /**< email address associated with login, or empty string */
+ char ip[72]; /**< ip address session was started from */
+ unsigned int mask; /**< access rights mask associated with session
+ * see enum lwsgs_auth_bits */
+ char session[42]; /**< session id string, usable as opaque uid when not logged in */
+};
+
+/** enum lws_gs_event */
+enum lws_gs_event {
+ LWSGSE_CREATED, /**< a new user was created */
+ LWSGSE_DELETED /**< an existing user was deleted */
+};
+
+/** struct lws_gs_event_args */
+struct lws_gs_event_args {
+ enum lws_gs_event event; /**< which event happened */
+ const char *username; /**< which username the event happened to */
+ const char *email; /**< the email address of that user */
+};
+
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*! \defgroup Protocols-and-Plugins Protocols and Plugins
+ * \ingroup lwsapi
+ *
+ * ##Protocol and protocol plugin -related apis
+ *
+ * Protocols bind ws protocol names to a custom callback specific to that
+ * protocol implementaion.
+ *
+ * A list of protocols can be passed in at context creation time, but it is
+ * also legal to leave that NULL and add the protocols and their callback code
+ * using plugins.
+ *
+ * Plugins are much preferable compared to cut and pasting code into an
+ * application each time, since they can be used standalone.
+ */
+///@{
+/** struct lws_protocols - List of protocols and handlers client or server
+ * supports. */
+
+struct lws_protocols {
+ const char *name;
+ /**< Protocol name that must match the one given in the client
+ * Javascript new WebSocket(url, 'protocol') name. */
+ lws_callback_function *callback;
+ /**< The service callback used for this protocol. It allows the
+ * service action for an entire protocol to be encapsulated in
+ * the protocol-specific callback */
+ size_t per_session_data_size;
+ /**< Each new connection using this protocol gets
+ * this much memory allocated on connection establishment and
+ * freed on connection takedown. A pointer to this per-connection
+ * allocation is passed into the callback in the 'user' parameter */
+ size_t rx_buffer_size;
+ /**< lws allocates this much space for rx data and informs callback
+ * when something came. Due to rx flow control, the callback may not
+ * be able to consume it all without having to return to the event
+ * loop. That is supported in lws.
+ *
+ * If .tx_packet_size is 0, this also controls how much may be sent at
+ * once for backwards compatibility.
+ */
+ unsigned int id;
+ /**< ignored by lws, but useful to contain user information bound
+ * to the selected protocol. For example if this protocol was
+ * called "myprotocol-v2", you might set id to 2, and the user
+ * code that acts differently according to the version can do so by
+ * switch (wsi->protocol->id), user code might use some bits as
+ * capability flags based on selected protocol version, etc. */
+ void *user; /**< ignored by lws, but user code can pass a pointer
+ here it can later access from the protocol callback */
+ size_t tx_packet_size;
+ /**< 0 indicates restrict send() size to .rx_buffer_size for backwards-
+ * compatibility.
+ * If greater than zero, a single send() is restricted to this amount
+ * and any remainder is buffered by lws and sent afterwards also in
+ * these size chunks. Since that is expensive, it's preferable
+ * to restrict one fragment you are trying to send to match this
+ * size.
+ */
+
+ /* Add new things just above here ---^
+ * This is part of the ABI, don't needlessly break compatibility */
+};
+
+/**
+ * lws_vhost_name_to_protocol() - get vhost's protocol object from its name
+ *
+ * \param vh: vhost to search
+ * \param name: protocol name
+ *
+ * Returns NULL or a pointer to the vhost's protocol of the requested name
+ */
+LWS_VISIBLE LWS_EXTERN const struct lws_protocols *
+lws_vhost_name_to_protocol(struct lws_vhost *vh, const char *name);
+
+/**
+ * lws_get_protocol() - Returns a protocol pointer from a websocket
+ * connection.
+ * \param wsi: pointer to struct websocket you want to know the protocol of
+ *
+ *
+ * Some apis can act on all live connections of a given protocol,
+ * this is how you can get a pointer to the active protocol if needed.
+ */
+LWS_VISIBLE LWS_EXTERN const struct lws_protocols *
+lws_get_protocol(struct lws *wsi);
+
+/** lws_protocol_get() - deprecated: use lws_get_protocol */
+LWS_VISIBLE LWS_EXTERN const struct lws_protocols *
+lws_protocol_get(struct lws *wsi) LWS_WARN_DEPRECATED;
+
+/**
+ * lws_protocol_vh_priv_zalloc() - Allocate and zero down a protocol's per-vhost
+ * storage
+ * \param vhost: vhost the instance is related to
+ * \param prot: protocol the instance is related to
+ * \param size: bytes to allocate
+ *
+ * Protocols often find it useful to allocate a per-vhost struct, this is a
+ * helper to be called in the per-vhost init LWS_CALLBACK_PROTOCOL_INIT
+ */
+LWS_VISIBLE LWS_EXTERN void *
+lws_protocol_vh_priv_zalloc(struct lws_vhost *vhost,
+ const struct lws_protocols *prot, int size);
+
+/**
+ * lws_protocol_vh_priv_get() - retreive a protocol's per-vhost storage
+ *
+ * \param vhost: vhost the instance is related to
+ * \param prot: protocol the instance is related to
+ *
+ * Recover a pointer to the allocated per-vhost storage for the protocol created
+ * by lws_protocol_vh_priv_zalloc() earlier
+ */
+LWS_VISIBLE LWS_EXTERN void *
+lws_protocol_vh_priv_get(struct lws_vhost *vhost,
+ const struct lws_protocols *prot);
+
+/**
+ * lws_adjust_protocol_psds - change a vhost protocol's per session data size
+ *
+ * \param wsi: a connection with the protocol to change
+ * \param new_size: the new size of the per session data size for the protocol
+ *
+ * Returns user_space for the wsi, after allocating
+ *
+ * This should not be used except to initalize a vhost protocol's per session
+ * data size one time, before any connections are accepted.
+ *
+ * Sometimes the protocol wraps another protocol and needs to discover and set
+ * its per session data size at runtime.
+ */
+LWS_VISIBLE LWS_EXTERN void *
+lws_adjust_protocol_psds(struct lws *wsi, size_t new_size);
+
+/**
+ * lws_finalize_startup() - drop initial process privileges
+ *
+ * \param context: lws context
+ *
+ * This is called after the end of the vhost protocol initializations, but
+ * you may choose to call it earlier
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_finalize_startup(struct lws_context *context);
+
+/**
+ * lws_pvo_search() - helper to find a named pvo in a linked-list
+ *
+ * \param pvo: the first pvo in the linked-list
+ * \param name: the name of the pvo to return if found
+ *
+ * Returns NULL, or a pointer to the name pvo in the linked-list
+ */
+LWS_VISIBLE LWS_EXTERN const struct lws_protocol_vhost_options *
+lws_pvo_search(const struct lws_protocol_vhost_options *pvo, const char *name);
+
+/**
+ * lws_pvo_get_str() - retreive a string pvo value
+ *
+ * \param in: the first pvo in the linked-list
+ * \param name: the name of the pvo to return if found
+ * \param result: pointer to a const char * to get the result if any
+ *
+ * Returns 0 if found and *result set, or nonzero if not found
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_pvo_get_str(void *in, const char *name, const char **result);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_protocol_init(struct lws_context *context);
+
+#ifdef LWS_WITH_PLUGINS
+
+/* PLUGINS implies LIBUV */
+
+#define LWS_PLUGIN_API_MAGIC 180
+
+/** struct lws_plugin_capability - how a plugin introduces itself to lws */
+struct lws_plugin_capability {
+ unsigned int api_magic; /**< caller fills this in, plugin fills rest */
+ const struct lws_protocols *protocols; /**< array of supported protocols provided by plugin */
+ int count_protocols; /**< how many protocols */
+ const struct lws_extension *extensions; /**< array of extensions provided by plugin */
+ int count_extensions; /**< how many extensions */
+};
+
+typedef int (*lws_plugin_init_func)(struct lws_context *,
+ struct lws_plugin_capability *);
+typedef int (*lws_plugin_destroy_func)(struct lws_context *);
+
+/** struct lws_plugin */
+struct lws_plugin {
+ struct lws_plugin *list; /**< linked list */
+#if (UV_VERSION_MAJOR > 0)
+ uv_lib_t lib; /**< shared library pointer */
+#endif
+ void *l; /**< so we can compile on ancient libuv */
+ char name[64]; /**< name of the plugin */
+ struct lws_plugin_capability caps; /**< plugin capabilities */
+};
+
+#endif
+
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*! \defgroup pur Sanitize / purify SQL and JSON helpers
+ *
+ * ##Sanitize / purify SQL and JSON helpers
+ *
+ * APIs for escaping untrusted JSON and SQL safely before use
+ */
+//@{
+
+/**
+ * lws_sql_purify() - like strncpy but with escaping for sql quotes
+ *
+ * \param escaped: output buffer
+ * \param string: input buffer ('/0' terminated)
+ * \param len: output buffer max length
+ *
+ * Because escaping expands the output string, it's not
+ * possible to do it in-place, ie, with escaped == string
+ */
+LWS_VISIBLE LWS_EXTERN const char *
+lws_sql_purify(char *escaped, const char *string, int len);
+
+/**
+ * lws_sql_purify_len() - return length of purified version of input string
+ *
+ * \param string: input buffer ('/0' terminated)
+ *
+ * Calculates any character escaping without writing it anywhere and returns the
+ * calculated length of the purified string.
+ */
+int
+lws_sql_purify_len(const char *p);
+
+/**
+ * lws_json_purify() - like strncpy but with escaping for json chars
+ *
+ * \param escaped: output buffer
+ * \param string: input buffer ('/0' terminated)
+ * \param len: output buffer max length
+ * \param in_used: number of bytes of string we could escape in len
+ *
+ * Because escaping expands the output string, it's not
+ * possible to do it in-place, ie, with escaped == string
+ */
+LWS_VISIBLE LWS_EXTERN const char *
+lws_json_purify(char *escaped, const char *string, int len, int *in_used);
+
+/**
+ * lws_json_purify_len() - find out the escaped length of a string
+ *
+ * \param string: input buffer ('/0' terminated)
+ *
+ * JSON may have to expand escapes by up to 6x the original depending on what
+ * it is. This doesn't actually do the escaping but goes through the motions
+ * and computes the length of the escaped string.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_json_purify_len(const char *string);
+
+/**
+ * lws_filename_purify_inplace() - replace scary filename chars with underscore
+ *
+ * \param filename: filename to be purified
+ *
+ * Replace scary characters in the filename (it should not be a path)
+ * with underscore, so it's safe to use.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_filename_purify_inplace(char *filename);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf,
+ int len);
+LWS_VISIBLE LWS_EXTERN int
+lws_plat_write_file(const char *filename, void *buf, int len);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_plat_read_file(const char *filename, void *buf, int len);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_plat_recommended_rsa_bits(void);
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+typedef struct lws_retry_bo {
+ const uint32_t *retry_ms_table; /* base delay in ms */
+ uint16_t retry_ms_table_count; /* entries in table */
+ uint16_t conceal_count; /* max retries to conceal */
+ uint16_t secs_since_valid_ping; /* idle before PING issued */
+ uint16_t secs_since_valid_hangup; /* idle before hangup conn */
+ uint8_t jitter_percent; /* % additional random jitter */
+} lws_retry_bo_t;
+
+#define LWS_RETRY_CONCEAL_ALWAYS (0xffff)
+
+/**
+ * lws_retry_get_delay_ms() - get next delay from backoff table
+ *
+ * \param lws_context: the lws context (used for getting random)
+ * \param retry: the retry backoff table we are using, or NULL for default
+ * \param ctry: pointer to the try counter
+ * \param conceal: pointer to flag set to nonzero if the try should be concealed
+ * in terms of creating an error
+ *
+ * Increments *\p try and retruns the number of ms that should elapse before the
+ * next connection retry, according to the backoff table \p retry. *\p conceal is
+ * set if the number of tries is less than the backoff table conceal_count, or
+ * is zero if it exceeded it. This lets you conceal a certain number of retries
+ * before alerting the caller there is a problem.
+ *
+ * If \p retry is NULL, a default of 3s + (0..300ms jitter) is used. If it's
+ * non-NULL but jitter_percent is 0, the default of 30% jitter is retained.
+ */
+
+LWS_VISIBLE LWS_EXTERN unsigned int
+lws_retry_get_delay_ms(struct lws_context *context, const lws_retry_bo_t *retry,
+ uint16_t *ctry, char *conceal);
+
+/**
+ * lws_retry_sul_schedule() - schedule a sul according to the backoff table
+ *
+ * \param lws_context: the lws context (used for getting random)
+ * \param sul: pointer to the sul to schedule
+ * \param retry: the retry backoff table we are using, or NULL for default
+ * \param cb: the callback for when the sul schedule time arrives
+ * \param ctry: pointer to the try counter
+ *
+ * Helper that combines interpreting the retry table with scheduling a sul to
+ * the computed delay. If conceal is not set, it will not schedule the sul
+ * and just return 1. Otherwise the sul is scheduled and it returns 0.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_retry_sul_schedule(struct lws_context *context, int tid,
+ lws_sorted_usec_list_t *sul, const lws_retry_bo_t *retry,
+ sul_cb_t cb, uint16_t *ctry);
+
+/**
+ * lws_retry_sul_schedule_retry_wsi() - retry sul schedule helper using wsi
+ *
+ * \param wsi: the wsi to set the hrtimer sul on to the next retry interval
+ * \param sul: pointer to the sul to schedule
+ * \param cb: the callback for when the sul schedule time arrives
+ * \param ctry: pointer to the try counter
+ *
+ * Helper that uses context, tid and retry policy from a wsi to call
+ * lws_retry_sul_schedule.
+ *
+ * Since a udp connection can have many writes in flight, the retry count and
+ * the sul used to track each thing that wants to be written have to be handled
+ * individually, not the wsi. But the retry policy and the other things can
+ * be filled in from the wsi conveniently.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_retry_sul_schedule_retry_wsi(struct lws *wsi, lws_sorted_usec_list_t *sul,
+ sul_cb_t cb, uint16_t *ctry);
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/** \defgroup lws_ring LWS Ringbuffer APIs
+ * ##lws_ring: generic ringbuffer struct
+ *
+ * Provides an abstract ringbuffer api supporting one head and one or an
+ * unlimited number of tails.
+ *
+ * All of the members are opaque and manipulated by lws_ring_...() apis.
+ *
+ * The lws_ring and its buffer is allocated at runtime on the heap, using
+ *
+ * - lws_ring_create()
+ * - lws_ring_destroy()
+ *
+ * It may contain any type, the size of the "element" stored in the ring
+ * buffer and the number of elements is given at creation time.
+ *
+ * When you create the ringbuffer, you can optionally provide an element
+ * destroy callback that frees any allocations inside the element. This is then
+ * automatically called for elements with no tail behind them, ie, elements
+ * which don't have any pending consumer are auto-freed.
+ *
+ * Whole elements may be inserted into the ringbuffer and removed from it, using
+ *
+ * - lws_ring_insert()
+ * - lws_ring_consume()
+ *
+ * You can find out how many whole elements are free or waiting using
+ *
+ * - lws_ring_get_count_free_elements()
+ * - lws_ring_get_count_waiting_elements()
+ *
+ * In addition there are special purpose optional byte-centric apis
+ *
+ * - lws_ring_next_linear_insert_range()
+ * - lws_ring_bump_head()
+ *
+ * which let you, eg, read() directly into the ringbuffer without needing
+ * an intermediate bounce buffer.
+ *
+ * The accessors understand that the ring wraps, and optimizes insertion and
+ * consumption into one or two memcpy()s depending on if the head or tail
+ * wraps.
+ *
+ * lws_ring only supports a single head, but optionally multiple tails with
+ * an API to inform it when the "oldest" tail has moved on. You can give
+ * NULL where-ever an api asks for a tail pointer, and it will use an internal
+ * single tail pointer for convenience.
+ *
+ * The "oldest tail", which is the only tail if you give it NULL instead of
+ * some other tail, is used to track which elements in the ringbuffer are
+ * still unread by anyone.
+ *
+ * - lws_ring_update_oldest_tail()
+ */
+///@{
+struct lws_ring;
+
+/**
+ * lws_ring_create(): create a new ringbuffer
+ *
+ * \param element_len: the size in bytes of one element in the ringbuffer
+ * \param count: the number of elements the ringbuffer can contain
+ * \param destroy_element: NULL, or callback to be called for each element
+ * that is removed from the ringbuffer due to the
+ * oldest tail moving beyond it
+ *
+ * Creates the ringbuffer and allocates the storage. Returns the new
+ * lws_ring *, or NULL if the allocation failed.
+ *
+ * If non-NULL, destroy_element will get called back for every element that is
+ * retired from the ringbuffer after the oldest tail has gone past it, and for
+ * any element still left in the ringbuffer when it is destroyed. It replaces
+ * all other element destruction code in your user code.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_ring *
+lws_ring_create(size_t element_len, size_t count,
+ void (*destroy_element)(void *element));
+
+/**
+ * lws_ring_destroy(): destroy a previously created ringbuffer
+ *
+ * \param ring: the struct lws_ring to destroy
+ *
+ * Destroys the ringbuffer allocation and the struct lws_ring itself.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_ring_destroy(struct lws_ring *ring);
+
+/**
+ * lws_ring_get_count_free_elements(): return how many elements can fit
+ * in the free space
+ *
+ * \param ring: the struct lws_ring to report on
+ *
+ * Returns how much room is left in the ringbuffer for whole element insertion.
+ */
+LWS_VISIBLE LWS_EXTERN size_t
+lws_ring_get_count_free_elements(struct lws_ring *ring);
+
+/**
+ * lws_ring_get_count_waiting_elements(): return how many elements can be consumed
+ *
+ * \param ring: the struct lws_ring to report on
+ * \param tail: a pointer to the tail struct to use, or NULL for single tail
+ *
+ * Returns how many elements are waiting to be consumed from the perspective
+ * of the tail pointer given.
+ */
+LWS_VISIBLE LWS_EXTERN size_t
+lws_ring_get_count_waiting_elements(struct lws_ring *ring, uint32_t *tail);
+
+/**
+ * lws_ring_insert(): attempt to insert up to max_count elements from src
+ *
+ * \param ring: the struct lws_ring to report on
+ * \param src: the array of elements to be inserted
+ * \param max_count: the number of available elements at src
+ *
+ * Attempts to insert as many of the elements at src as possible, up to the
+ * maximum max_count. Returns the number of elements actually inserted.
+ */
+LWS_VISIBLE LWS_EXTERN size_t
+lws_ring_insert(struct lws_ring *ring, const void *src, size_t max_count);
+
+/**
+ * lws_ring_consume(): attempt to copy out and remove up to max_count elements
+ * to src
+ *
+ * \param ring: the struct lws_ring to report on
+ * \param tail: a pointer to the tail struct to use, or NULL for single tail
+ * \param dest: the array of elements to be inserted. or NULL for no copy
+ * \param max_count: the number of available elements at src
+ *
+ * Attempts to copy out as many waiting elements as possible into dest, from
+ * the perspective of the given tail, up to max_count. If dest is NULL, the
+ * copying out is not done but the elements are logically consumed as usual.
+ * NULL dest is useful in combination with lws_ring_get_element(), where you
+ * can use the element direct from the ringbuffer and then call this with NULL
+ * dest to logically consume it.
+ *
+ * Increments the tail position according to how many elements could be
+ * consumed.
+ *
+ * Returns the number of elements consumed.
+ */
+LWS_VISIBLE LWS_EXTERN size_t
+lws_ring_consume(struct lws_ring *ring, uint32_t *tail, void *dest,
+ size_t max_count);
+
+/**
+ * lws_ring_get_element(): get a pointer to the next waiting element for tail
+ *
+ * \param ring: the struct lws_ring to report on
+ * \param tail: a pointer to the tail struct to use, or NULL for single tail
+ *
+ * Points to the next element that tail would consume, directly in the
+ * ringbuffer. This lets you write() or otherwise use the element without
+ * having to copy it out somewhere first.
+ *
+ * After calling this, you must call lws_ring_consume(ring, &tail, NULL, 1)
+ * which will logically consume the element you used up and increment your
+ * tail (tail may also be NULL there if you use a single tail).
+ *
+ * Returns NULL if no waiting element, or a const void * pointing to it.
+ */
+LWS_VISIBLE LWS_EXTERN const void *
+lws_ring_get_element(struct lws_ring *ring, uint32_t *tail);
+
+/**
+ * lws_ring_update_oldest_tail(): free up elements older than tail for reuse
+ *
+ * \param ring: the struct lws_ring to report on
+ * \param tail: a pointer to the tail struct to use, or NULL for single tail
+ *
+ * If you are using multiple tails, you must use this API to inform the
+ * lws_ring when none of the tails still need elements in the fifo any more,
+ * by updating it when the "oldest" tail has moved on.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_ring_update_oldest_tail(struct lws_ring *ring, uint32_t tail);
+
+/**
+ * lws_ring_get_oldest_tail(): get current oldest available data index
+ *
+ * \param ring: the struct lws_ring to report on
+ *
+ * If you are initializing a new ringbuffer consumer, you can set its tail to
+ * this to start it from the oldest ringbuffer entry still available.
+ */
+LWS_VISIBLE LWS_EXTERN uint32_t
+lws_ring_get_oldest_tail(struct lws_ring *ring);
+
+/**
+ * lws_ring_next_linear_insert_range(): used to write directly into the ring
+ *
+ * \param ring: the struct lws_ring to report on
+ * \param start: pointer to a void * set to the start of the next ringbuffer area
+ * \param bytes: pointer to a size_t set to the max length you may use from *start
+ *
+ * This provides a low-level, bytewise access directly into the ringbuffer
+ * allowing direct insertion of data without having to use a bounce buffer.
+ *
+ * The api reports the position and length of the next linear range that can
+ * be written in the ringbuffer, ie, up to the point it would wrap, and sets
+ * *start and *bytes accordingly. You can then, eg, directly read() into
+ * *start for up to *bytes, and use lws_ring_bump_head() to update the lws_ring
+ * with what you have done.
+ *
+ * Returns nonzero if no insertion is currently possible.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_ring_next_linear_insert_range(struct lws_ring *ring, void **start,
+ size_t *bytes);
+
+/**
+ * lws_ring_bump_head(): used to write directly into the ring
+ *
+ * \param ring: the struct lws_ring to operate on
+ * \param bytes: the number of bytes you inserted at the current head
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_ring_bump_head(struct lws_ring *ring, size_t bytes);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_ring_dump(struct lws_ring *ring, uint32_t *tail);
+
+/*
+ * This is a helper that combines the common pattern of needing to consume
+ * some ringbuffer elements, move the consumer tail on, and check if that
+ * has moved any ringbuffer elements out of scope, because it was the last
+ * consumer that had not already consumed them.
+ *
+ * Elements that go out of scope because the oldest tail is now after them
+ * get garbage-collected by calling the destroy_element callback on them
+ * defined when the ringbuffer was created.
+ */
+
+#define lws_ring_consume_and_update_oldest_tail(\
+ ___ring, /* the lws_ring object */ \
+ ___type, /* type of objects with tails */ \
+ ___ptail, /* ptr to tail of obj with tail doing consuming */ \
+ ___count, /* count of payload objects being consumed */ \
+ ___list_head, /* head of list of objects with tails */ \
+ ___mtail, /* member name of tail in ___type */ \
+ ___mlist /* member name of next list member ptr in ___type */ \
+ ) { \
+ int ___n, ___m; \
+ \
+ ___n = lws_ring_get_oldest_tail(___ring) == *(___ptail); \
+ lws_ring_consume(___ring, ___ptail, NULL, ___count); \
+ if (___n) { \
+ uint32_t ___oldest; \
+ ___n = 0; \
+ ___oldest = *(___ptail); \
+ lws_start_foreach_llp(___type **, ___ppss, ___list_head) { \
+ ___m = (int)lws_ring_get_count_waiting_elements( \
+ ___ring, &(*___ppss)->___mtail); \
+ if (___m >= ___n) { \
+ ___n = ___m; \
+ ___oldest = (*___ppss)->___mtail; \
+ } \
+ } lws_end_foreach_llp(___ppss, ___mlist); \
+ \
+ lws_ring_update_oldest_tail(___ring, ___oldest); \
+ } \
+}
+
+/*
+ * This does the same as the lws_ring_consume_and_update_oldest_tail()
+ * helper, but for the simpler case there is only one consumer, so one
+ * tail, and that tail is always the oldest tail.
+ */
+
+#define lws_ring_consume_single_tail(\
+ ___ring, /* the lws_ring object */ \
+ ___ptail, /* ptr to tail of obj with tail doing consuming */ \
+ ___count /* count of payload objects being consumed */ \
+ ) { \
+ lws_ring_consume(___ring, ___ptail, NULL, ___count); \
+ lws_ring_update_oldest_tail(___ring, *(___ptail)); \
+}
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * This is the headers for secure stream api variants that deal with clients in
+ * different threads or even different processes.
+ *
+ * lws_ss_ when client is directly using the event loop
+ * lws_sstc_ when client is in a different thread to the event loop
+ * lws_sspc_ when client is in a different process to the event loop
+ *
+ * The client api is almost the same except the slightly diffent names.
+ */
+
+/*
+ * lws_sspc_ apis... different process
+ */
+
+/*
+ * Helper translation so user code written to lws_ss_ can be built for
+ * lws_sspc_ in one step by #define LWS_SS_USE_SSPC before including
+ */
+
+#if defined(LWS_SS_USE_SSPC)
+#define lws_ss_handle lws_sspc_handle
+#define lws_ss_create lws_sspc_create
+#define lws_ss_destroy lws_sspc_destroy
+#define lws_ss_request_tx lws_sspc_request_tx
+#define lws_ss_client_connect lws_sspc_client_connect
+#define lws_ss_get_sequencer lws_sspc_get_sequencer
+#define lws_ss_proxy_create lws_sspc_proxy_create
+#define lws_ss_get_context lws_sspc_get_context
+#define lws_ss_rideshare lws_sspc_rideshare
+#define lws_ss_set_metadata lws_sspc_set_metadata
+#define lws_ss_add_peer_tx_credit lws_sspc_add_peer_tx_credit
+#define lws_ss_get_est_peer_tx_credit lws_sspc_get_est_peer_tx_credit
+#endif
+
+
+struct lws_sspc_handle;
+
+LWS_VISIBLE LWS_EXTERN int
+lws_sspc_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi,
+ void *opaque_user_data, struct lws_sspc_handle **ppss,
+ struct lws_sequencer *seq_owner, const char **ppayload_fmt);
+
+/**
+ * lws_sspc_destroy() - Destroy secure stream
+ *
+ * \param ppss: pointer to lws_ss_t pointer to be destroyed
+ *
+ * Destroys the lws_ss_t pointed to by *ppss, and sets *ppss to NULL.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_sspc_destroy(struct lws_sspc_handle **ppss);
+
+/**
+ * lws_sspc_request_tx() - Schedule stream for tx
+ *
+ * \param pss: pointer to lws_ss_t representing stream that wants to transmit
+ *
+ * Schedules a write on the stream represented by \p pss. When it's possible to
+ * write on this stream, the *tx callback will occur with an empty buffer for
+ * the stream owner to fill in.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_sspc_request_tx(struct lws_sspc_handle *pss);
+
+/**
+ * lws_sspc_client_connect() - Attempt the client connect
+ *
+ * \param h: secure streams handle
+ *
+ * Starts the connection process for the secure stream. Returns 0 if OK or
+ * nonzero if we have already failed.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_sspc_client_connect(struct lws_sspc_handle *h);
+
+/**
+ * lws_sspc_get_sequencer() - Return parent sequencer pointer if any
+ *
+ * \param h: secure streams handle
+ *
+ * Returns NULL if the secure stream is not associated with a sequencer.
+ * Otherwise returns a pointer to the owning sequencer. You can use this to
+ * identify which sequencer to direct messages to, from the secure stream
+ * callback.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_sequencer *
+lws_sspc_get_sequencer(struct lws_sspc_handle *h);
+
+/**
+ * lws_sspc_proxy_create() - Start a unix domain socket proxy for Secure Streams
+ *
+ * \param context: lws_context
+ *
+ * Creates a vhost that listens on an abstract namespace unix domain socket at
+ * address "proxy.ss.lws". Client connections to this proxy to Secure Streams
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_sspc_proxy_create(struct lws_context *context);
+
+/**
+ * lws_ss_get_context() - convenience helper to recover the lws context
+ *
+ * \h: secure streams handle
+ *
+ * Returns the lws context. Dispenses with the need to pass a copy of it into
+ * your secure streams handler.
+ */
+
+LWS_VISIBLE LWS_EXTERN struct lws_context *
+lws_sspc_get_context(struct lws_sspc_handle *h);
+
+LWS_VISIBLE LWS_EXTERN const struct lws_protocols lws_sspc_protocols[];
+
+LWS_VISIBLE LWS_EXTERN const char *
+lws_sspc_rideshare(struct lws_sspc_handle *h);
+
+
+/**
+ * lws_sspc_set_metadata() - allow user to bind external data to defined ss metadata
+ *
+ * \h: secure streams handle
+ * \name: metadata name from the policy
+ * \value: pointer to user-managed data to bind to name
+ * \len: length of the user-managed data in value
+ *
+ * Binds user-managed data to the named metadata item from the ss policy.
+ * If present, the metadata item is handled in a protocol-specific way using
+ * the associated policy information. For example, in the policy
+ *
+ * "\"metadata\":" "["
+ * "{\"uptag\":" "\"X-Upload-Tag:\"},"
+ * "{\"ctype\":" "\"Content-Type:\"},"
+ * "{\"xctype\":" "\"X-Content-Type:\"}"
+ * "],"
+ *
+ * when the policy is using h1 is interpreted to add h1 headers of the given
+ * name with the value of the metadata on the left.
+ *
+ * Return 0 if OK.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_sspc_set_metadata(struct lws_sspc_handle *h, const char *name,
+ void *value, size_t len);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_sspc_add_peer_tx_credit(struct lws_sspc_handle *h, int32_t add);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_sspc_get_est_peer_tx_credit(struct lws_sspc_handle *h);
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * included from libwebsockets.h
+ */
+
+typedef int (*plugin_auth_status_cb)(struct lws_ss_handle *ss, int status);
+
+/**
+ * lws_ss_plugin_auth_t - api for an auth plugin
+ *
+ * Auth plugins create and sequence authenticated connections that can carry one
+ * or more streams to an endpoint. That may involve other connections to other
+ * places to eg, gather authenticated tokens and then make the real connection
+ * using the tokens.
+ *
+ * The secure stream object contains members to record which auth plugin the
+ * stream is bound to and an over-allocation of the secure stream object to
+ * contain the plugin auth private data.
+ *
+ * The auth plugin controls the state of the stream connection via the status
+ * callback, and handles retries.
+ *
+ * Network connections may require one kind of auth sequencing, and streams
+ * inside those connections another kind of auth sequencing depending on their
+ * role. So the secure stream object allows defining plugins for both kinds.
+ *
+ * Streams may disappear at any time and require reauth to bring a new one up.
+ * The auth plugin sequencer will connect / reconnect either on demand, or from
+ * the start and after any connectivity loss if any stream using the connection
+ * has the LWSSSPOLF_NAILED_UP flag.
+ */
+
+typedef struct lws_ss_plugin {
+ struct lws_ss_plugin *next;
+ const char *name; /**< auth plugin name */
+ size_t alloc; /**< size of private allocation */
+
+ int (*create)(struct lws_ss_handle *ss, void *info,
+ plugin_auth_status_cb status);
+ /**< called when the auth plugin is instantiated
+ and bound to the secure stream. status is
+ called back with advisory information about
+ the authenticated stream state as it
+ proceeds */
+ int (*destroy)(struct lws_ss_handle *ss);
+ /**< called when the related secure stream is
+ being destroyed, and anything the auth
+ plugin is doing should also be destroyed */
+ int (*munge)(struct lws_ss_handle *ss, char *path,
+ size_t path_len);
+ /**< if the plugin needs to munge transactions
+ that have metadata outside the payload (eg,
+ add http headers) this callback will give
+ it the opportunity to do so */
+} lws_ss_plugin_t;
+
+
+typedef struct lws_ss_x509 {
+ struct lws_ss_x509 *next;
+ const char *vhost_name; /**< vhost name using cert ctx */
+ const uint8_t *ca_der; /**< DER x.509 cert */
+ size_t ca_der_len; /**< length of DER cert */
+} lws_ss_x509_t;
+
+enum {
+ LWSSSPOLF_OPPORTUNISTIC = (1 << 0),
+ /**< the connection doesn't exist unless client asks to write */
+ LWSSSPOLF_NAILED_UP = (1 << 1),
+ /**< the connection tries to be connected the whole life of the ss */
+ LWSSSPOLF_URGENT_TX = (1 << 2),
+ /**< this connection carries critical tx data */
+ LWSSSPOLF_URGENT_RX = (1 << 3),
+ /**< this connection carries critical rx data */
+ LWSSSPOLF_TLS = (1 << 4),
+ /**< stream must be connected via a tls tunnel */
+ LWSSSPOLF_LONG_POLL = (1 << 5),
+ /**< stream used to receive async rx at arbitrary intervals */
+ LWSSSPOLF_AUTH_BEARER = (1 << 6),
+ /**< for http, use lws_system auth token 0 in authentication: bearer */
+ LWSSSPOLF_HTTP_NO_CONTENT_LENGTH = (1 << 7),
+ /**< don't add any content length even if we have it */
+ LWSSSPOLF_QUIRK_NGHTTP2_END_STREAM = (1 << 8),
+ /**< set the client flag LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM */
+ LWSSSPOLF_H2_QUIRK_OVERFLOWS_TXCR = (1 << 9),
+ /**< set the client flag LCCSCF_H2_QUIRK_OVERFLOWS_TXCR */
+ LWSSSPOLF_H2_QUIRK_UNCLEAN_HPACK_STATE = (1 << 10),
+ /**< HPACK decoder state does not end cleanly */
+ LWSSSPOLF_HTTP_MULTIPART = (1 << 11),
+ /**< indicates stream goes out as specifically a multipart mime POST
+ * section... if the tx has LWSSS_FLAG_COALESCE_CONTINUES flag then more
+ * multipart sections are expected. Without it, the multipart wrapper
+ * is closed and the http transaction issue completed when this message
+ * finishes. */
+ LWSSSPOLF_HTTP_X_WWW_FORM_URLENCODED = (1 << 12),
+ /**< set up lws_system client cert */
+ LWSSSPOLF_LOCAL_SINK = (1 << 13),
+ /**< expected to bind to a local sink only */
+};
+
+typedef struct lws_ss_trust_store {
+ struct lws_ss_trust_store *next;
+ const char *name;
+
+ lws_ss_x509_t *ssx509[8];
+ int count;
+} lws_ss_trust_store_t;
+
+enum {
+ LWSSSP_H1,
+ LWSSSP_H2,
+ LWSSSP_WS,
+
+
+ LWSSS_HBI_AUTH = 0,
+ LWSSS_HBI_DSN,
+ LWSSS_HBI_FWV,
+ LWSSS_HBI_TYPE,
+
+ _LWSSS_HBI_COUNT /* always last */
+};
+
+typedef struct lws_ss_metadata {
+ struct lws_ss_metadata *next;
+ const char *name;
+ void *value;
+ size_t length;
+
+ uint8_t value_on_lws_heap; /* proxy does this */
+} lws_ss_metadata_t;
+
+
+/**
+ * lws_ss_policy_t: policy database entry for a stream type
+ *
+ * Decides the system policy for how to implement connections of name
+ * .streamtype.
+ *
+ * Streams may need one kind of auth sequencing for the network connection and
+ * another kind of auth sequencing for the streams that are carried inside it,
+ * this is the purpose of .nauth and .sauth. Both are optional and may be NULL.
+ *
+ * An array of these is set at context creation time, ending with one with a
+ * NULL streamtype.
+ */
+typedef struct lws_ss_policy {
+ struct lws_ss_policy *next;
+ const char *streamtype; /**< stream type lhs to match on */
+
+ const char *endpoint; /**< DNS address to connect to */
+ const char *rideshare_streamtype; /**< optional transport
+ * on another, preexisting stream of this
+ * streamtype name */
+ const char *payload_fmt;
+ const char *socks5_proxy;
+ lws_ss_metadata_t *metadata; /* linked-list of metadata */
+
+ /* protocol-specific connection policy details */
+
+ union {
+
+ /* details for http-related protocols... */
+
+ struct {
+
+ /* common to all http-related protocols */
+
+ const char *method;
+ const char *url;
+
+ const char *multipart_name;
+ const char *multipart_filename;
+ const char *multipart_content_type;
+
+ const char *blob_header[_LWSSS_HBI_COUNT];
+ const char *auth_preamble;
+
+ union {
+// struct { /* LWSSSP_H1 */
+// } h1;
+// struct { /* LWSSSP_H2 */
+// } h2;
+ struct { /* LWSSSP_WS */
+ const char *subprotocol;
+ uint8_t binary;
+ /* false = TEXT, true = BINARY */
+ } ws;
+ } u;
+ } http;
+
+ struct {
+ const char *topic; /* stream sends on this topic */
+ const char *subscribe; /* stream subscribes to this topic */
+
+ const char *will_topic;
+ const char *will_message;
+
+ uint16_t keep_alive;
+ uint8_t qos;
+ uint8_t clean_start;
+ uint8_t will_qos;
+ uint8_t will_retain;
+
+ } mqtt;
+
+ /* details for non-http related protocols... */
+ } u;
+
+ const
+ struct lws_ss_plugin *plugins[2]; /**< NULL or auth plugin */
+ const void *plugins_info[2]; /**< plugin-specific data */
+
+ const lws_ss_trust_store_t *trust_store; /**< CA certs needed for conn
+ validation, only set between policy parsing and vhost creation */
+
+ const lws_retry_bo_t *retry_bo; /**< retry policy to use */
+
+ uint32_t flags; /**< stream attribute flags */
+
+ uint16_t port; /**< endpoint port */
+
+ uint8_t metadata_count; /**< metadata count */
+ uint8_t protocol; /**< protocol index */
+ uint8_t client_cert; /**< which client cert to apply
+ 0 = none, 1+ = cc 0+ */
+} lws_ss_policy_t;
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * included from libwebsockets.h
+ *
+ *
+ * Secure Streams is a *payload-only* client communication channel where all the
+ * details about the connection are held in a systemwide policy database and
+ * are keyed by the streamtype field... the user of the communication channel
+ * does not know or manage the choice of endpoint, tls CA, or even wire
+ * protocol. The advantage is he then does not have any dependency on any of
+ * those and they can be changed just by changing the policy database without
+ * touching the code using the stream.
+ *
+ * There are two ways secure streams interfaces to user code:
+ *
+ * 1) [Linux / RTOS] the natural, smallest interface is to call back to user
+ * code that only operates directly from the lws event loop thread context
+ * (direct callbacks from lws_ss_t)
+ *
+ * lws_thread( [user code] ---- lws )
+ *
+ * 2) [Linux] where the user code is in a different process and communicates
+ * asynchronously via a proxy socket
+ *
+ * user_process{ [user code] | shim | socket-}------ lws_process{ lws }
+ *
+ * In the second, IPC, case, all packets are prepended by one or more bytes
+ * indicating the packet type and serializing any associated data, known as
+ * Serialized Secure Streams or SSS.
+ *
+ * Serialized Secure Streams
+ * -------------------------
+ *
+ * On the transport, adjacent packets may be coalesced, that is, the original
+ * packet sizes are lost and two or more packets are combined. For that reason
+ * the serialization format always contains a 1-byte type and then a 2-byte
+ * frame length.
+ *
+ * Client to proxy
+ *
+ * - Proxied connection setup
+ *
+ * - 0: LWSSS_SER_TXPRE_STREAMTYPE
+ * - 1: 2-byte MSB-first rest-of-frame length
+ * - 3: 4 byte MSB-first initial tx credit
+ * - 7: the streamtype name with no NUL
+ *
+ * - Proxied tx
+ *
+ * - 0: LWSSS_SER_TXPRE_TX_PAYLOAD
+ * - 1: 2 byte MSB-first rest-of-frame length
+ * - 3: 4-byte MSB-first flags
+ * - 7: 4-byte MSB-first us between client requested write and wrote to proxy
+ * - 11: 8-byte MSB-first us resolution unix time client wrote to proxy
+ * - 17: payload
+ *
+ * - Proxied secure stream destroy
+ *
+ * - 0: LWSSS_SER_TXPRE_DESTROYING
+ * - 1: 00, 00
+ *
+ * - Proxied metadata - sent when one metadata item set clientside
+ *
+ * - 0: LWSSS_SER_TXPRE_METADATA
+ * - 1: 2-byte MSB-first rest-of-frame length
+ * - 2: 1-byte metadata name length
+ * - 3: metadata name
+ * - ...: metadata value (for rest of packet)
+ *
+ * Proxy to client
+ *
+ * - Proxied connection setup result
+ *
+ * - 0: LWSSS_SER_RXPRE_CREATE_RESULT
+ * - 1: 2 byte MSB-first rest-of-frame length (usually 00, 03)
+ * - 3: 1 byte result, 0 = success. On failure, proxy will close connection.
+ * - 4: 2 byte MSB-first initial tx credit
+ * - 6: if present, comma-sep list of rideshare types from policy
+ *
+ * - Proxied rx
+ *
+ * - 0: LWSSS_SER_RXPRE_RX_PAYLOAD
+ * - 1: 2 byte MSB-first rest-of-frame length
+ * - 3: 4-byte MSB-first flags
+ * - 7: 4-byte MSB-first us between inbound read and wrote to client
+ * - 11: 8-byte MSB-first us resolution unix time proxy wrote to client
+ * - 17: (rideshare name len + rideshare name if flags & LWSSS_FLAG_RIDESHARE)
+ * payload
+ *
+ * - Proxied tx credit
+ *
+ * - 0: LWSSS_SER_RXPRE_TXCR_UPDATE
+ * - 1: 00, 04
+ * - 3: 4-byte MSB-first addition tx credit bytes
+ *
+ * - Proxied state
+ *
+ * - 0: LWSSS_SER_RXPRE_CONNSTATE
+ * - 1: 00, 05
+ * - 3: 1 byte state index
+ * - 7: 4-byte MSB-first ordinal
+ *
+ *
+ * Proxied tx may be read by the proxy but rejected due to lack of buffer space
+ * at the proxy. For that reason, tx must be held at the sender until it has
+ * been acknowledged or denied.
+ *
+ * Sinks
+ * -----
+ *
+ * Sinks are logical "servers", you can register as a sink for a particular
+ * streamtype by using the lws_ss_create() api with ssi->register_sink set to 1.
+ *
+ * For directly fulfilled Secure Streams, new streams of that streamtype bind
+ * to the rx, tx and state handlers given when it was registered.
+ *
+ * - When new streams are created the registered sink handler for (*state) is
+ * called with event LWSSSCS_SINK_JOIN and the new client stream handle in
+ * the h_src parameter.
+ *
+ * - When the client stream sends something to the sink, it calls the sink's
+ * (*rx) with the client stream's
+ */
+
+#define LWS_SS_MTU 1540
+
+struct lws_ss_handle;
+typedef uint32_t lws_ss_tx_ordinal_t;
+
+/*
+ * connection state events
+ */
+typedef enum {
+ LWSSSCS_CREATING,
+ LWSSSCS_DISCONNECTED,
+ LWSSSCS_UNREACHABLE,
+ LWSSSCS_AUTH_FAILED,
+ LWSSSCS_CONNECTED,
+ LWSSSCS_CONNECTING,
+ LWSSSCS_DESTROYING,
+ LWSSSCS_POLL,
+ LWSSSCS_ALL_RETRIES_FAILED, /* all retries in bo policy failed */
+ LWSSSCS_QOS_ACK_REMOTE, /* remote peer received and acked tx */
+ LWSSSCS_QOS_NACK_REMOTE,
+ LWSSSCS_QOS_ACK_LOCAL, /* local proxy accepted our tx */
+ LWSSSCS_QOS_NACK_LOCAL, /* local proxy refused our tx */
+
+ LWSSSCS_SINK_JOIN, /* sinks get this when a new source
+ * stream joins the sink */
+ LWSSSCS_SINK_PART, /* sinks get this when a new source
+ * stream leaves the sink */
+} lws_ss_constate_t;
+
+enum {
+ LWSSS_FLAG_SOM = (1 << 0),
+ /* payload contains the start of new message */
+ LWSSS_FLAG_EOM = (1 << 1),
+ /* payload contains the end of message */
+ LWSSS_FLAG_POLL = (1 << 2),
+ /* Not a real transmit... poll for rx if protocol needs it */
+ LWSSS_FLAG_RELATED_START = (1 << 3),
+ /* Appears in a zero-length message indicating a message group of zero
+ * or more messages is now starting. */
+ LWSSS_FLAG_RELATED_END = (1 << 4),
+ /* Appears in a zero-length message indicating a message group of zero
+ * or more messages has now finished. */
+ LWSSS_FLAG_RIDESHARE = (1 << 5),
+ /* Serialized payload starts with non-default rideshare name length and
+ * name string without NUL, then payload */
+
+ /*
+ * In the case the secure stream is proxied across a process or thread
+ * boundary, eg by proxying through a socket for IPC, metadata must be
+ * carried in-band. A byte is prepended to each rx payload to
+ * differentiate what it is.
+ *
+ * Secure streams where the user is called back directly does not need
+ * any of this and only pure payloads are passed.
+ *
+ * rx (received by client) prepends for proxied connections
+ */
+
+ LWSSS_SER_RXPRE_RX_PAYLOAD = 0x55,
+ LWSSS_SER_RXPRE_CREATE_RESULT,
+ LWSSS_SER_RXPRE_CONNSTATE,
+ LWSSS_SER_RXPRE_TXCR_UPDATE,
+ LWSSS_SER_RXPRE_TLSNEG_ENCLAVE_SIGN,
+
+ /* tx (send by client) prepends for proxied connections */
+
+ LWSSS_SER_TXPRE_STREAMTYPE = 0xaa,
+ LWSSS_SER_TXPRE_ONWARD_CONNECT,
+ LWSSS_SER_TXPRE_DESTROYING,
+ LWSSS_SER_TXPRE_TX_PAYLOAD,
+ LWSSS_SER_TXPRE_METADATA,
+ LWSSS_SER_TXPRE_TXCR_UPDATE,
+ LWSSS_SER_TXPRE_TLSNEG_ENCLAVE_SIGNED,
+};
+
+typedef enum {
+ LPCS_WAIT_INITIAL_TX = 1, /* after connect, must send streamtype */
+ LPCS_REPORTING_FAIL, /* stream creation failed, wait to to tell */
+ LPCS_REPORTING_OK, /* stream creation succeeded, wait to to tell */
+ LPCS_OPERATIONAL, /* ready for payloads */
+ LPCS_DESTROYED,
+
+ LPCS_SENDING_INITIAL_TX = 1, /* after connect, must send streamtype */
+ LPCS_WAITING_CREATE_RESULT, /* wait to hear if proxy ss create OK */
+ LPCS_LOCAL_CONNECTED, /* we are in touch with the proxy */
+ LPCS_ONWARD_CONNECT, /* request onward ss connection */
+
+} lws_ss_conn_states_t;
+
+/**
+ * lws_ss_info_t: information about stream to be created
+ *
+ * Prepare this struct with information about what the stream type is and how
+ * the stream should interface with your code, and pass it to lws_ss_create()
+ * to create the requested stream.
+ */
+
+typedef struct lws_ss_info {
+ const char *streamtype; /**< type of stream we want to create */
+ size_t user_alloc; /**< size of user allocation */
+ size_t handle_offset; /**< offset of handle stg in user_alloc type,
+ set to offsetof(mytype, my_handle_member) */
+ size_t opaque_user_data_offset;
+ /**< offset of opaque user data ptr in user_alloc type, set to
+ offsetof(mytype, opaque_ud_member) */
+
+ int (*rx)(void *userobj, const uint8_t *buf, size_t len,
+ int flags);
+ /**< callback with rx payload for this stream */
+ int (*tx)(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf,
+ size_t *len, int *flags);
+ /**< callback to send payload on this stream... 0 = send as set in
+ * len and flags, 1 = do not send anything (ie, not even 0 len frame) */
+ int (*state)(void *userobj, void *h_src /* ss handle type */,
+ lws_ss_constate_t state, lws_ss_tx_ordinal_t ack);
+ /**< advisory cb about state of stream and QoS status if applicable...
+ * h_src is only used with sinks and LWSSSCS_SINK_JOIN/_PART events.
+ * Return nonzero to indicate you want to destroy the stream. */
+ int manual_initial_tx_credit;
+ /**< 0 = manage any tx credit automatically, nonzero explicitly sets the
+ * peer stream to have the given amount of tx credit, if the protocol
+ * can support it. */
+ char register_sink;
+ /**< If set, we're not creating a specific stream, but registering
+ * ourselves as the "sink" for .streamtype. It's analogous to saying
+ * we want to be the many-to-one "server" for .streamtype; when other
+ * streams are created with that streamtype, they should be forwarded
+ * to this stream owner, where they join and part from the sink via
+ * (*state) LWSSSCS_SINK_JOIN / _PART events, the new client handle
+ * being provided in the h_src parameter.
+ */
+} lws_ss_info_t;
+
+/**
+ * lws_ss_create() - Create secure stream
+ *
+ * \param context: the lws context to create this inside
+ * \param tsi: service thread index to create on (normally 0)
+ * \param ssi: pointer to lws_ss_info_t filled in with info about desired stream
+ * \param opaque_user_data: opaque data to set in the stream's user object
+ * \param ppss: pointer to secure stream handle pointer set on exit
+ * \param ppayload_fmt: NULL or pointer to a string ptr to take payload format
+ * name from the policy
+ *
+ * Requests a new secure stream described by \p ssi be created. If successful,
+ * the stream is created, its state callback called with LWSSSCS_CREATING, *ppss
+ * is set to point to the handle, and it returns 0. If it failed, it returns
+ * nonzero.
+ *
+ * Along with the opaque stream object, streams overallocate
+ *
+ * 1) a user data struct whose size is set in ssi
+ * 2) nauth plugin instantiation data (size set in the plugin struct)
+ * 3) sauth plugin instantiation data (size set in the plugin struct)
+ * 4) space for a copy of the stream type name
+ *
+ * The user data struct is initialized to all zeros, then the .handle_offset and
+ * .opaque_user_data_offset fields of the ssi are used to prepare the user data
+ * struct with the ss handle that was created, and a copy of the
+ * opaque_user_data pointer given as an argument.
+ *
+ * If you want to set up the stream with specific information, point to it in
+ * opaque_user_data and use the copy of that pointer in your user data member
+ * for it starting from the LWSSSCS_CREATING state call.
+ *
+ * Since different endpoints chosen by the policy may require different payload
+ * formats, \p ppayload_fmt is set to point to the name of the needed payload
+ * format from the policy database if non-NULL.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_ss_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi,
+ void *opaque_user_data, struct lws_ss_handle **ppss,
+ struct lws_sequencer *seq_owner, const char **ppayload_fmt);
+
+/**
+ * lws_ss_destroy() - Destroy secure stream
+ *
+ * \param ppss: pointer to lws_ss_t pointer to be destroyed
+ *
+ * Destroys the lws_ss_t pointed to by *ppss, and sets *ppss to NULL.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_ss_destroy(struct lws_ss_handle **ppss);
+
+/**
+ * lws_ss_request_tx() - Schedule stream for tx
+ *
+ * \param pss: pointer to lws_ss_t representing stream that wants to transmit
+ *
+ * Schedules a write on the stream represented by \p pss. When it's possible to
+ * write on this stream, the *tx callback will occur with an empty buffer for
+ * the stream owner to fill in.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_ss_request_tx(struct lws_ss_handle *pss);
+
+/**
+ * lws_ss_request_tx() - Schedule stream for tx
+ *
+ * \param pss: pointer to lws_ss_t representing stream that wants to transmit
+ * \param len: the length of the write in bytes
+ *
+ * Schedules a write on the stream represented by \p pss. When it's possible to
+ * write on this stream, the *tx callback will occur with an empty buffer for
+ * the stream owner to fill in.
+ *
+ * This api variant should be used when it's possible the payload will go out
+ * over h1 with x-web-form-urlencoded or similar Content-Type.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_ss_request_tx_len(struct lws_ss_handle *pss, unsigned long len);
+
+
+/**
+ * lws_ss_client_connect() - Attempt the client connect
+ *
+ * \param h: secure streams handle
+ *
+ * Starts the connection process for the secure stream. Returns 0 if OK or
+ * nonzero if we have already failed.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_ss_client_connect(struct lws_ss_handle *h);
+
+/**
+ * lws_ss_get_sequencer() - Return parent sequencer pointer if any
+ *
+ * \param h: secure streams handle
+ *
+ * Returns NULL if the secure stream is not associated with a sequencer.
+ * Otherwise returns a pointer to the owning sequencer. You can use this to
+ * identify which sequencer to direct messages to, from the secure stream
+ * callback.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_sequencer *
+lws_ss_get_sequencer(struct lws_ss_handle *h);
+
+/**
+ * lws_ss_proxy_create() - Start a unix domain socket proxy for Secure Streams
+ *
+ * \param context: lws_context
+ * \param bind: if port is 0, unix domain path with leading @ for abstract.
+ * if port nonzero, NULL, or network interface to bind listen to
+ * \param port: tcp port to listen on
+ *
+ * Creates a vhost that listens either on an abstract namespace unix domain
+ * socket (port = 0) or a tcp listen socket (port nonzero). If bind is NULL
+ * and port is 0, the abstract unix domain socket defaults to "proxy.ss.lws".
+ *
+ * Client connections to this proxy to Secure Streams are fulfilled using the
+ * policy local to the proxy and the data passed between the client and the
+ * proxy using serialized Secure Streams protocol.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_ss_proxy_create(struct lws_context *context, const char *bind, int port);
+
+/**
+ * lws_ss_state_name() - convenience helper to get a printable conn state name
+ *
+ * \param state: the connection state index
+ *
+ * Returns a printable name for the connection state index passed in.
+ */
+LWS_VISIBLE LWS_EXTERN const char *
+lws_ss_state_name(int state);
+
+/**
+ * lws_ss_get_context() - convenience helper to recover the lws context
+ *
+ * \param h: secure streams handle
+ *
+ * Returns the lws context. Dispenses with the need to pass a copy of it into
+ * your secure streams handler.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_context *
+lws_ss_get_context(struct lws_ss_handle *h);
+
+/**
+ * lws_ss_rideshare() - find the current streamtype when types rideshare
+ *
+ * \param h: the stream handle
+ *
+ * Under some conditions, the payloads may be structured using protocol-
+ * specific formatting, eg, http multipart mime. It's possible to map the
+ * logical partitions in the payload to different stream types using
+ * the policy "rideshare" feature.
+ *
+ * This api lets the callback code find out which rideshare stream type the
+ * current payload chunk belongs to.
+ */
+LWS_VISIBLE LWS_EXTERN const char *
+lws_ss_rideshare(struct lws_ss_handle *h);
+
+
+/**
+ * lws_ss_set_metadata() - allow user to bind external data to defined ss metadata
+ *
+ * \param h: secure streams handle
+ * \param name: metadata name from the policy
+ * \param value: pointer to user-managed data to bind to name
+ * \param len: length of the user-managed data in value
+ *
+ * Binds user-managed data to the named metadata item from the ss policy.
+ * If present, the metadata item is handled in a protocol-specific way using
+ * the associated policy information. For example, in the policy
+ *
+ * "\"metadata\":" "["
+ * "{\"uptag\":" "\"X-Upload-Tag:\"},"
+ * "{\"ctype\":" "\"Content-Type:\"},"
+ * "{\"xctype\":" "\"\"}"
+ * "],"
+ *
+ * when the policy is using h1 is interpreted to add h1 headers of the given
+ * name with the value of the metadata on the left.
+ *
+ * Return 0 if OK or nonzero if, eg, metadata name does not exist on the
+ * streamtype.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_ss_set_metadata(struct lws_ss_handle *h, const char *name,
+ void *value, size_t len);
+
+
+/**
+ * lws_ss_add_peer_tx_credit() - allow peer to transmit more to us
+ *
+ * \param h: secure streams handle
+ * \param add: additional tx credit (signed)
+ *
+ * Indicate to remote peer that we can accept \p add bytes more payload being
+ * sent to us.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_ss_add_peer_tx_credit(struct lws_ss_handle *h, int32_t add);
+
+/**
+ * lws_ss_get_est_peer_tx_credit() - get our current estimate of peer's tx credit
+ *
+ * \param h: secure streams handle
+ *
+ * Based on what credit we gave it, and what we have received, report our
+ * estimate of peer's tx credit usable to transmit to us. This may be outdated
+ * in that some or all of its credit may already have been expended by sending
+ * stuff to us that is in flight already.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_ss_get_est_peer_tx_credit(struct lws_ss_handle *h);
--- /dev/null
+ /*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * lws_sequencer is intended to help implement sequences that:
+ *
+ * - outlive a single connection lifetime,
+ * - are not associated with a particular protocol,
+ * - are not associated with a particular vhost,
+ * - must receive and issue events inside the event loop
+ *
+ * lws_sequencer-s are bound to a pt (per-thread) which for the default case of
+ * one service thread is the same as binding to an lws_context.
+ */
+/*
+ * retry backoff table... retry n happens after .retry_ms_table[n] ms, with
+ * the last entry used if n is greater than the number of entries.
+ *
+ * The first .conceal_count retries are concealed, but after that the failures
+ * are reported.
+ */
+
+typedef enum {
+ LWSSEQ_CREATED, /* sequencer created */
+ LWSSEQ_DESTROYED, /* sequencer destroyed */
+ LWSSEQ_TIMED_OUT, /* sequencer timeout */
+ LWSSEQ_HEARTBEAT, /* 1Hz callback */
+
+ LWSSEQ_WSI_CONNECTED, /* wsi we bound to us has connected */
+ LWSSEQ_WSI_CONN_FAIL, /* wsi we bound to us has failed to connect */
+ LWSSEQ_WSI_CONN_CLOSE, /* wsi we bound to us has closed */
+
+
+ LWSSEQ_SS_STATE_BASE, /* secure streams owned by a sequencer provide
+ * automatic messages about state changes on
+ * the sequencer, passing the oridinal in the
+ * event argument field. The message index is
+ * LWSSEQ_SS_STATE_BASE + the enum from
+ * lws_ss_constate_t */
+
+ LWSSEQ_USER_BASE = 100 /* define your events from here */
+} lws_seq_events_t;
+
+typedef enum lws_seq_cb_return {
+ LWSSEQ_RET_CONTINUE,
+ LWSSEQ_RET_DESTROY
+} lws_seq_cb_return_t;
+
+/*
+ * handler for this sequencer. Return 0 if OK else nonzero to destroy the
+ * sequencer. LWSSEQ_DESTROYED will be called back to the handler so it can
+ * close / destroy any private assets associated with the sequence.
+ *
+ * The callback may return either LWSSEQ_RET_CONTINUE for the sequencer to
+ * resume or LWSSEQ_RET_DESTROY to indicate the sequence is finished.
+ *
+ * Event indexes consist of some generic ones but mainly user-defined ones
+ * starting from LWSSEQ_USER_BASE.
+ */
+typedef lws_seq_cb_return_t (*lws_seq_event_cb)(struct lws_sequencer *seq,
+ void *user, int event, void *data, void *aux);
+
+typedef struct lws_seq_info {
+ struct lws_context *context; /* lws_context for seq */
+ int tsi; /* thread service idx */
+ size_t user_size; /* size of user alloc */
+ void **puser; /* place ptr to user */
+ lws_seq_event_cb cb; /* seq callback */
+ const char *name; /* seq name */
+ const lws_retry_bo_t *retry; /* retry policy */
+} lws_seq_info_t;
+
+/**
+ * lws_seq_create() - create and bind sequencer to a pt
+ *
+ * \param info: information about sequencer to create
+ *
+ * This binds an abstract sequencer to a per-thread (by default, the single
+ * event loop of an lws_context). After the event loop starts, the sequencer
+ * will receive an LWSSEQ_CREATED event on its callback from the event loop
+ * context, where it can begin its sequence flow.
+ *
+ * Lws itself will only call the callback subsequently with LWSSEQ_DESTROYED
+ * when the sequencer is being destroyed.
+ *
+ * pt locking is used to protect the related data structures.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_sequencer *
+lws_seq_create(lws_seq_info_t *info);
+
+/**
+ * lws_seq_destroy() - destroy the sequencer
+ *
+ * \param seq: pointer to the the opaque sequencer pointer returned by
+ * lws_seq_create()
+ *
+ * This proceeds to destroy the sequencer, calling LWSSEQ_DESTROYED and then
+ * freeing the sequencer object itself. The pointed-to seq pointer will be
+ * set to NULL.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_seq_destroy(struct lws_sequencer **seq);
+
+/**
+ * lws_seq_queue_event() - queue an event on the given sequencer
+ *
+ * \param seq: the opaque sequencer pointer returned by lws_seq_create()
+ * \param e: the event index to queue
+ * \param data: associated opaque (to lws) data to provide the callback
+ * \param aux: second opaque data to provide the callback
+ *
+ * This queues the event on a given sequencer. Queued events are delivered one
+ * per sequencer each subsequent time around the event loop, so the cb is called
+ * from the event loop thread context.
+ *
+ * Notice that because the events are delivered in order from the event loop,
+ * the scope of objects pointed to by \p data or \p aux may exceed the lifetime
+ * of the thing containing the pointed-to data. So it's usually better to pass
+ * values here.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_seq_queue_event(struct lws_sequencer *seq, lws_seq_events_t e, void *data,
+ void *aux);
+
+/**
+ * lws_seq_check_wsi() - check if wsi still extant
+ *
+ * \param seq: the sequencer interested in the wsi
+ * \param wsi: the wsi we want to confirm hasn't closed yet
+ *
+ * Check if wsi still extant, by peeking in the message queue for a
+ * LWSSEQ_WSI_CONN_CLOSE message about wsi. (Doesn't need to do the same for
+ * CONN_FAIL since that will never have produced any messages prior to that).
+ *
+ * Use this to avoid trying to perform operations on wsi that have already
+ * closed but we didn't get to that message yet.
+ *
+ * Returns 0 if not closed yet or 1 if it has closed but we didn't process the
+ * close message yet.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_seq_check_wsi(struct lws_sequencer *seq, struct lws *wsi);
+
+#define LWSSEQTO_NONE 0
+
+/**
+ * lws_seq_timeout_us() - set a timeout by which the sequence must have
+ * completed by a different event or inform the
+ * sequencer
+ *
+ * \param seq: The sequencer to set the timeout on
+ * \param us: How many us in the future to fire the timeout
+ * LWS_SET_TIMER_USEC_CANCEL = cancel any existing timeout
+ *
+ * This api allows the sequencer to ask to be informed if it has not completed
+ * or disabled its timeout after secs seconds. Lws will send a LWSSEQ_TIMED_OUT
+ * event to the sequencer if the timeout expires.
+ *
+ * Typically the sequencer sets the timeout when starting a step, then waits to
+ * hear a queued event informing it the step completed or failed. The timeout
+ * provides a way to deal with the case the step neither completed nor failed
+ * within the timeout period.
+ *
+ * Lws wsi timeouts are not really suitable for this since they are focused on
+ * short-term protocol timeout protection and may be set and reset many times
+ * in one transaction. Wsi timeouts also enforce closure of the wsi when they
+ * trigger, sequencer timeouts have no side effect except to queue the
+ * LWSSEQ_TIMED_OUT message and leave it to the sequencer to decide how to
+ * react appropriately.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_seq_timeout_us(struct lws_sequencer *seq, lws_usec_t us);
+
+/**
+ * lws_seq_from_user(): get the lws_seq_t pointer from the user ptr
+ *
+ * \param u: the sequencer user allocation returned by lws_seq_create() or
+ * provided in the sequencer callback
+ *
+ * This gets the lws_seq_t * from the sequencer user allocation pointer.
+ * Actually these are allocated at the same time in one step, with the user
+ * allocation immediately after the lws_seq_t, so lws can compute where
+ * the lws_seq_t is from having the user allocation pointer. Since the
+ * size of the lws_seq_t is unknown to user code, this helper does it for
+ * you.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_sequencer *
+lws_seq_from_user(void *u);
+
+/**
+ * lws_seq_us_since_creation(): elapsed seconds since sequencer created
+ *
+ * \param seq: pointer to the lws_seq_t
+ *
+ * Returns the number of us elapsed since the lws_seq_t was
+ * created. This is useful to calculate sequencer timeouts for the current
+ * step considering a global sequencer lifetime limit.
+ */
+LWS_VISIBLE LWS_EXTERN lws_usec_t
+lws_seq_us_since_creation(struct lws_sequencer *seq);
+
+/**
+ * lws_seq_name(): get the name of this sequencer
+ *
+ * \param seq: pointer to the lws_seq_t
+ *
+ * Returns the name given when the sequencer was created. This is useful to
+ * annotate logging when then are multiple sequencers in play.
+ */
+LWS_VISIBLE LWS_EXTERN const char *
+lws_seq_name(struct lws_sequencer *seq);
+
+/**
+ * lws_seq_get_context(): get the lws_context sequencer was created on
+ *
+ * \param seq: pointer to the lws_seq_t
+ *
+ * Returns the lws_context. Saves you having to store it if you have a seq
+ * pointer handy.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_context *
+lws_seq_get_context(struct lws_sequencer *seq);
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/** \defgroup service Built-in service loop entry
+ *
+ * ##Built-in service loop entry
+ *
+ * If you're not using libev / libuv, these apis are needed to enter the poll()
+ * wait in lws and service any connections with pending events.
+ */
+///@{
+
+/**
+ * lws_service() - Service any pending websocket activity
+ * \param context: Websocket context
+ * \param timeout_ms: Set to 0; ignored; for backward compatibility
+ *
+ * This function deals with any pending websocket traffic, for three
+ * kinds of event. It handles these events on both server and client
+ * types of connection the same.
+ *
+ * 1) Accept new connections to our context's server
+ *
+ * 2) Call the receive callback for incoming frame data received by
+ * server or client connections.
+ *
+ * Since v3.2 internally the timeout wait is ignored, the lws scheduler is
+ * smart enough to stay asleep until an event is queued.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_service(struct lws_context *context, int timeout_ms);
+
+/**
+ * lws_service_tsi() - Service any pending websocket activity
+ *
+ * \param context: Websocket context
+ * \param timeout_ms: Set to 0; ignored; for backwards compatibility
+ * \param tsi: Thread service index, starting at 0
+ *
+ * Same as lws_service(), but for a specific thread service index. Only needed
+ * if you are spawning multiple service threads.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_service_tsi(struct lws_context *context, int timeout_ms, int tsi);
+
+/**
+ * lws_cancel_service_pt() - Cancel servicing of pending socket activity
+ * on one thread
+ * \param wsi: Cancel service on the thread this wsi is serviced by
+ *
+ * Same as lws_cancel_service(), but targets a single service thread, the one
+ * the wsi belongs to. You probably want to use lws_cancel_service() instead.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_cancel_service_pt(struct lws *wsi);
+
+/**
+ * lws_cancel_service() - Cancel wait for new pending socket activity
+ * \param context: Websocket context
+ *
+ * This function creates an immediate "synchronous interrupt" to the lws poll()
+ * wait or event loop. As soon as possible in the serialzed service sequencing,
+ * a LWS_CALLBACK_EVENT_WAIT_CANCELLED callback is sent to every protocol on
+ * every vhost.
+ *
+ * lws_cancel_service() may be called from another thread while the context
+ * exists, and its effect will be immediately serialized.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_cancel_service(struct lws_context *context);
+
+/**
+ * lws_service_fd() - Service polled socket with something waiting
+ * \param context: Websocket context
+ * \param pollfd: The pollfd entry describing the socket fd and which events
+ * happened
+ *
+ * This function takes a pollfd that has POLLIN or POLLOUT activity and
+ * services it according to the state of the associated
+ * struct lws.
+ *
+ * The one call deals with all "service" that might happen on a socket
+ * including listen accepts, http files as well as websocket protocol.
+ *
+ * If a pollfd says it has something, you can just pass it to
+ * lws_service_fd() whether it is a socket handled by lws or not.
+ * If it sees it is a lws socket, the traffic will be handled and
+ * pollfd->revents will be zeroed now.
+ *
+ * If the socket is foreign to lws, it leaves revents alone. So you can
+ * see if you should service yourself by checking the pollfd revents
+ * after letting lws try to service it.
+ *
+ * lws before v3.2 allowed pollfd to be NULL, to indicate that background
+ * periodic processing should be done. Since v3.2, lws schedules any items
+ * that need handling in the future using lws_sul and NULL is no longer valid.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd);
+
+/**
+ * lws_service_fd_tsi() - Service polled socket in specific service thread
+ * \param context: Websocket context
+ * \param pollfd: The pollfd entry describing the socket fd and which events
+ * happened.
+ * \param tsi: thread service index
+ *
+ * Same as lws_service_fd() but used with multiple service threads
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
+ int tsi);
+
+/**
+ * lws_service_adjust_timeout() - Check for any connection needing forced service
+ * \param context: Websocket context
+ * \param timeout_ms: The original poll timeout value. You can just set this
+ * to 1 if you don't really have a poll timeout.
+ * \param tsi: thread service index
+ *
+ * Under some conditions connections may need service even though there is no
+ * pending network action on them, this is "forced service". For default
+ * poll() and libuv / libev, the library takes care of calling this and
+ * dealing with it for you. But for external poll() integration, you need
+ * access to the apis.
+ *
+ * If anybody needs "forced service", returned timeout is zero. In that case,
+ * you can call lws_service_tsi() with a timeout of -1 to only service
+ * guys who need forced service.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi);
+
+/* Backwards compatibility */
+#define lws_plat_service_tsi lws_service_tsi
+
+LWS_VISIBLE LWS_EXTERN int
+lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd);
+
+///@}
+
+/*! \defgroup uv libuv helpers
+ *
+ * ##libuv helpers
+ *
+ * APIs specific to libuv event loop itegration
+ */
+///@{
+#ifdef LWS_WITH_LIBUV
+/*
+ * Any direct libuv allocations in lws protocol handlers must participate in the
+ * lws reference counting scheme. Two apis are provided:
+ *
+ * - lws_libuv_static_refcount_add(handle, context) to mark the handle with
+ * a pointer to the context and increment the global uv object counter
+ *
+ * - lws_libuv_static_refcount_del() which should be used as the close callback
+ * for your own libuv objects declared in the protocol scope.
+ *
+ * Using the apis allows lws to detach itself from a libuv loop completely
+ * cleanly and at the moment all of its libuv objects have completed close.
+ */
+
+LWS_VISIBLE LWS_EXTERN uv_loop_t *
+lws_uv_getloop(struct lws_context *context, int tsi);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_libuv_static_refcount_add(uv_handle_t *, struct lws_context *context);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_libuv_static_refcount_del(uv_handle_t *);
+
+#endif /* LWS_WITH_LIBUV */
+
+#if defined(LWS_PLAT_FREERTOS)
+#define lws_libuv_static_refcount_add(_a, _b)
+#define lws_libuv_static_refcount_del NULL
+#endif
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/** \defgroup sha SHA and B64 helpers
+ * ##SHA and B64 helpers
+ *
+ * These provide SHA-1 and B64 helper apis
+ */
+///@{
+#ifdef LWS_SHA1_USE_OPENSSL_NAME
+#define lws_SHA1 SHA1
+#else
+/**
+ * lws_SHA1(): make a SHA-1 digest of a buffer
+ *
+ * \param d: incoming buffer
+ * \param n: length of incoming buffer
+ * \param md: buffer for message digest (must be >= 20 bytes)
+ *
+ * Reduces any size buffer into a 20-byte SHA-1 hash.
+ */
+LWS_VISIBLE LWS_EXTERN unsigned char *
+lws_SHA1(const unsigned char *d, size_t n, unsigned char *md);
+#endif
+/**
+ * lws_b64_encode_string(): encode a string into base 64
+ *
+ * \param in: incoming buffer
+ * \param in_len: length of incoming buffer
+ * \param out: result buffer
+ * \param out_size: length of result buffer
+ *
+ * Encodes a string using b64
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_b64_encode_string(const char *in, int in_len, char *out, int out_size);
+/**
+ * lws_b64_encode_string_url(): encode a string into base 64
+ *
+ * \param in: incoming buffer
+ * \param in_len: length of incoming buffer
+ * \param out: result buffer
+ * \param out_size: length of result buffer
+ *
+ * Encodes a string using b64 with the "URL" variant (+ -> -, and / -> _)
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_b64_encode_string_url(const char *in, int in_len, char *out, int out_size);
+/**
+ * lws_b64_decode_string(): decode a string from base 64
+ *
+ * \param in: incoming buffer
+ * \param out: result buffer
+ * \param out_size: length of result buffer
+ *
+ * Decodes a NUL-terminated string using b64
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_b64_decode_string(const char *in, char *out, int out_size);
+/**
+ * lws_b64_decode_string_len(): decode a string from base 64
+ *
+ * \param in: incoming buffer
+ * \param in_len: length of incoming buffer
+ * \param out: result buffer
+ * \param out_size: length of result buffer
+ *
+ * Decodes a range of chars using b64
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_b64_decode_string_len(const char *in, int in_len, char *out, int out_size);
+
+struct lws_b64state {
+ unsigned char quad[4];
+ size_t done;
+ size_t len;
+ int i;
+ int c;
+};
+
+LWS_VISIBLE LWS_EXTERN void
+lws_b64_decode_state_init(struct lws_b64state *state);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_b64_decode_stateful(struct lws_b64state *s, const char *in, size_t *in_len,
+ uint8_t *out, size_t *out_size, int final);
+///@}
+
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/** \defgroup form-parsing Form Parsing
+ * \ingroup http
+ * ##POSTed form parsing functions
+ *
+ * These lws_spa (stateful post arguments) apis let you parse and urldecode
+ * POSTed form arguments, both using simple urlencoded and multipart transfer
+ * encoding.
+ *
+ * It's capable of handling file uploads as well a named input parsing,
+ * and the apis are the same for both form upload styles.
+ *
+ * You feed it a list of parameter names and it creates pointers to the
+ * urldecoded arguments: file upload parameters pass the file data in chunks to
+ * a user-supplied callback as they come.
+ *
+ * Since it's stateful, it handles the incoming data needing more than one
+ * POST_BODY callback and has no limit on uploaded file size.
+ */
+///@{
+
+/** enum lws_spa_fileupload_states */
+enum lws_spa_fileupload_states {
+ LWS_UFS_CONTENT,
+ /**< a chunk of file content has arrived */
+ LWS_UFS_FINAL_CONTENT,
+ /**< the last chunk (possibly zero length) of file content has arrived */
+ LWS_UFS_OPEN,
+ /**< a new file is starting to arrive */
+ LWS_UFS_CLOSE
+ /**< the file decode stuff is being destroyed */
+};
+
+/**
+ * lws_spa_fileupload_cb() - callback to receive file upload data
+ *
+ * \param data: opt_data pointer set in lws_spa_create
+ * \param name: name of the form field being uploaded
+ * \param filename: original filename from client
+ * \param buf: start of data to receive
+ * \param len: length of data to receive
+ * \param state: information about how this call relates to file
+ *
+ * Notice name and filename shouldn't be trusted, as they are passed from
+ * HTTP provided by the client.
+ */
+typedef int (*lws_spa_fileupload_cb)(void *data, const char *name,
+ const char *filename, char *buf, int len,
+ enum lws_spa_fileupload_states state);
+
+/** struct lws_spa - opaque urldecode parser capable of handling multipart
+ * and file uploads */
+struct lws_spa;
+
+/**
+ * lws_spa_create() - create urldecode parser
+ *
+ * \param wsi: lws connection (used to find Content Type)
+ * \param param_names: array of form parameter names, like "username"
+ * \param count_params: count of param_names
+ * \param max_storage: total amount of form parameter values we can store
+ * \param opt_cb: NULL, or callback to receive file upload data.
+ * \param opt_data: NULL, or user pointer provided to opt_cb.
+ *
+ * Creates a urldecode parser and initializes it.
+ *
+ * It's recommended to use the newer api, lws_spa_create_via_info()
+ * instead.
+ *
+ * opt_cb can be NULL if you just want normal name=value parsing, however
+ * if one or more entries in your form are bulk data (file transfer), you
+ * can provide this callback and filter on the name callback parameter to
+ * treat that urldecoded data separately. The callback should return -1
+ * in case of fatal error, and 0 if OK.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_spa *
+lws_spa_create(struct lws *wsi, const char * const *param_names,
+ int count_params, int max_storage, lws_spa_fileupload_cb opt_cb,
+ void *opt_data);
+
+typedef struct lws_spa_create_info {
+ const char * const *param_names; /* array of form parameter names, like "username" */
+ int count_params; /* count of param_names */
+ int max_storage; /* total amount of form parameter values we can store */
+ lws_spa_fileupload_cb opt_cb; /* NULL, or callback to receive file upload data. */
+ void *opt_data; /* NULL, or user pointer provided to opt_cb. */
+ size_t param_names_stride; /* 0 if param_names is an array of char *.
+ Else stride to next char * */
+ struct lwsac **ac; /* NULL, or pointer to lwsac * to contain all
+ related heap allocations */
+ size_t ac_chunk_size; /* 0 for default, or ac chunk size */
+} lws_spa_create_info_t;
+
+/**
+ * lws_spa_create_via_info() - create urldecode parser
+ *
+ * \param wsi: lws connection (used to find Content Type)
+ * \param info: pointer to struct defining the arguments
+ *
+ * Creates a urldecode parser and initializes it.
+ *
+ * opt_cb can be NULL if you just want normal name=value parsing, however
+ * if one or more entries in your form are bulk data (file transfer), you
+ * can provide this callback and filter on the name callback parameter to
+ * treat that urldecoded data separately. The callback should return -1
+ * in case of fatal error, and 0 if OK.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_spa *
+lws_spa_create_via_info(struct lws *wsi, const lws_spa_create_info_t *info);
+
+/**
+ * lws_spa_process() - parses a chunk of input data
+ *
+ * \param spa: the parser object previously created
+ * \param in: incoming urlencoded data
+ * \param len: count of bytes valid at \p in
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_spa_process(struct lws_spa *spa, const char *in, int len);
+
+/**
+ * lws_spa_finalize() - indicate incoming data completed
+ *
+ * \param spa: the parser object previously created
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_spa_finalize(struct lws_spa *spa);
+
+/**
+ * lws_spa_get_length() - return length of parameter value
+ *
+ * \param spa: the parser object previously created
+ * \param n: parameter ordinal to return length of value for
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_spa_get_length(struct lws_spa *spa, int n);
+
+/**
+ * lws_spa_get_string() - return pointer to parameter value
+ * \param spa: the parser object previously created
+ * \param n: parameter ordinal to return pointer to value for
+ */
+LWS_VISIBLE LWS_EXTERN const char *
+lws_spa_get_string(struct lws_spa *spa, int n);
+
+/**
+ * lws_spa_destroy() - destroy parser object
+ *
+ * \param spa: the parser object previously created
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_spa_destroy(struct lws_spa *spa);
+///@}
--- /dev/null
+ /*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+struct lws_state_notify_link;
+struct lws_state_manager;
+
+typedef int (*lws_state_notify_t)(struct lws_state_manager *mgr,
+ struct lws_state_notify_link *link,
+ int current, int target);
+
+typedef struct lws_state_notify_link {
+ lws_dll2_t list;
+ lws_state_notify_t notify_cb;
+ const char *name;
+} lws_state_notify_link_t;
+
+typedef struct lws_state_manager {
+ lws_dll2_owner_t notify_list;
+ void *parent;
+ /**< optional opaque pointer to owning object... useful to make such
+ * a pointer available to a notification callback. Ignored by lws */
+ const char **state_names; /* may be NULL */
+ const char *name;
+ int state;
+} lws_state_manager_t;
+
+/**
+ * lws_state_reg_notifier() - add dep handler for state notifications
+ *
+ * \param context: the lws_context
+ * \param nl: the handler to add to the notifier linked-list
+ *
+ * Add \p notify_link to the context's list of notification handlers for system
+ * state changes. The handlers can defeat or take over responsibility for
+ * retrying the change after they have initiated some dependency.
+ */
+
+LWS_EXTERN LWS_VISIBLE void
+lws_state_reg_notifier(lws_state_manager_t *mgr, lws_state_notify_link_t *nl);
+
+/**
+ * lws_state_reg_deregister() - deregister a notifier
+ *
+ * \param nl: notification hardler to deregister
+ *
+ * Remove a notification handler from its state manager
+ */
+
+LWS_EXTERN LWS_VISIBLE void
+lws_state_reg_deregister(lws_state_notify_link_t *nl);
+
+/**
+ * lws_state_reg_notifier_list() - add dep handlers for state notifications
+ *
+ * \param context: the lws_context
+ * \param nl: list of notification handlers
+ *
+ * Add a NULL-terminated list of notification handler pointers to a notification
+ * manager object
+ */
+
+LWS_EXTERN LWS_VISIBLE void
+lws_state_reg_notifier_list(lws_state_manager_t *mgr,
+ lws_state_notify_link_t * const *nl);
+
+/**
+ * lws_state_transition_steps() - move to state via starting any deps
+ *
+ * \param mgr: the state manager object
+ * \param target: the state we wish to move to
+ *
+ * Advance state by state towards state \p target. At each state, notifiers
+ * may veto the change and be triggered to perform dependencies, stopping the
+ * advance towards the target state.
+ */
+LWS_EXTERN LWS_VISIBLE int
+lws_state_transition_steps(lws_state_manager_t *mgr, int target);
+
+/**
+ * lws_state_transition() - move to state via starting any deps
+ *
+ * \param mgr: the state manager object
+ * \param target: the state we wish to move to
+ *
+ * Jump to state target atomically. Notifiers may veto it.
+ */
+LWS_EXTERN LWS_VISIBLE int
+lws_state_transition(lws_state_manager_t *mgr, int target);
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*
+ * Stats are all uint64_t numbers that start at 0.
+ * Index names here have the convention
+ *
+ * _C_ counter
+ * _B_ byte count
+ * _MS_ millisecond count
+ */
+
+enum {
+ LWSSTATS_C_CONNECTIONS, /**< count incoming connections */
+ LWSSTATS_C_API_CLOSE, /**< count calls to close api */
+ LWSSTATS_C_API_READ, /**< count calls to read from socket api */
+ LWSSTATS_C_API_LWS_WRITE, /**< count calls to lws_write API */
+ LWSSTATS_C_API_WRITE, /**< count calls to write API */
+ LWSSTATS_C_WRITE_PARTIALS, /**< count of partial writes */
+ LWSSTATS_C_WRITEABLE_CB_REQ, /**< count of writable callback requests */
+ LWSSTATS_C_WRITEABLE_CB_EFF_REQ, /**< count of effective writable callback requests */
+ LWSSTATS_C_WRITEABLE_CB, /**< count of writable callbacks */
+ LWSSTATS_C_SSL_CONNECTIONS_FAILED, /**< count of failed SSL connections */
+ LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED, /**< count of accepted SSL connections */
+ LWSSTATS_C_SSL_ACCEPT_SPIN, /**< count of SSL_accept() attempts */
+ LWSSTATS_C_SSL_CONNS_HAD_RX, /**< count of accepted SSL conns that have had some RX */
+ LWSSTATS_C_TIMEOUTS, /**< count of timed-out connections */
+ LWSSTATS_C_SERVICE_ENTRY, /**< count of entries to lws service loop */
+ LWSSTATS_B_READ, /**< aggregate bytes read */
+ LWSSTATS_B_WRITE, /**< aggregate bytes written */
+ LWSSTATS_B_PARTIALS_ACCEPTED_PARTS, /**< aggreate of size of accepted write data from new partials */
+ LWSSTATS_US_SSL_ACCEPT_LATENCY_AVG, /**< aggregate delay in accepting connection */
+ LWSSTATS_US_WRITABLE_DELAY_AVG, /**< aggregate delay between asking for writable and getting cb */
+ LWSSTATS_US_WORST_WRITABLE_DELAY, /**< single worst delay between asking for writable and getting cb */
+ LWSSTATS_US_SSL_RX_DELAY_AVG, /**< aggregate delay between ssl accept complete and first RX */
+ LWSSTATS_C_PEER_LIMIT_AH_DENIED, /**< number of times we would have given an ah but for the peer limit */
+ LWSSTATS_C_PEER_LIMIT_WSI_DENIED, /**< number of times we would have given a wsi but for the peer limit */
+ LWSSTATS_C_CONNS_CLIENT, /**< attempted client conns */
+ LWSSTATS_C_CONNS_CLIENT_FAILED, /**< failed client conns */
+
+ /* Add new things just above here ---^
+ * This is part of the ABI, don't needlessly break compatibility
+ *
+ * UPDATE stat_names in stats.c in sync with this!
+ */
+ LWSSTATS_SIZE
+};
+
+#if defined(LWS_WITH_STATS)
+
+LWS_VISIBLE LWS_EXTERN uint64_t
+lws_stats_get(struct lws_context *context, int index);
+LWS_VISIBLE LWS_EXTERN void
+lws_stats_log_dump(struct lws_context *context);
+#else
+static LWS_INLINE uint64_t
+lws_stats_get(struct lws_context *context, int index) { (void)context; (void)index; return 0; }
+static LWS_INLINE void
+lws_stats_log_dump(struct lws_context *context) { (void)context; }
+#endif
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#if defined(LWS_WITH_STRUCT_SQLITE3)
+#include <sqlite3.h>
+#endif
+
+typedef enum {
+ LSMT_SIGNED,
+ LSMT_UNSIGNED,
+ LSMT_BOOLEAN,
+ LSMT_STRING_CHAR_ARRAY,
+ LSMT_STRING_PTR,
+ LSMT_LIST,
+ LSMT_CHILD_PTR,
+ LSMT_SCHEMA,
+
+} lws_struct_map_type_eum;
+
+typedef struct lejp_collation {
+ struct lws_dll2 chunks;
+ int len;
+ char buf[LEJP_STRING_CHUNK + 1];
+} lejp_collation_t;
+
+typedef struct lws_struct_map {
+ const char *colname;
+ const struct lws_struct_map *child_map;
+ lejp_callback lejp_cb;
+ size_t ofs; /* child dll2; points to dll2_owner */
+ size_t aux;
+ size_t ofs_clist;
+ size_t child_map_size;
+ lws_struct_map_type_eum type;
+} lws_struct_map_t;
+
+typedef int (*lws_struct_args_cb)(void *obj, void *cb_arg);
+
+typedef struct lws_struct_args {
+ const lws_struct_map_t *map_st[LEJP_MAX_PARSING_STACK_DEPTH];
+ lws_struct_args_cb cb;
+ struct lwsac *ac;
+ void *cb_arg;
+ void *dest;
+
+ size_t dest_len;
+ size_t toplevel_dll2_ofs;
+ size_t map_entries_st[LEJP_MAX_PARSING_STACK_DEPTH];
+ size_t ac_block_size;
+ int subtype;
+
+ int top_schema_index;
+
+ /*
+ * temp ac used to collate unknown possibly huge strings before final
+ * allocation and copy
+ */
+ struct lwsac *ac_chunks;
+ struct lws_dll2_owner chunks_owner;
+ size_t chunks_length;
+} lws_struct_args_t;
+
+#define LSM_SIGNED(type, name, qname) \
+ { \
+ qname, \
+ NULL, \
+ NULL, \
+ offsetof(type, name), \
+ sizeof ((type *)0)->name, \
+ 0, \
+ 0, \
+ LSMT_SIGNED \
+ }
+
+#define LSM_UNSIGNED(type, name, qname) \
+ { \
+ qname, \
+ NULL, \
+ NULL, \
+ offsetof(type, name), \
+ sizeof ((type *)0)->name, \
+ 0, \
+ 0, \
+ LSMT_UNSIGNED \
+ }
+
+#define LSM_BOOLEAN(type, name, qname) \
+ { \
+ qname, \
+ NULL, \
+ NULL, \
+ offsetof(type, name), \
+ sizeof ((type *)0)->name, \
+ 0, \
+ 0, \
+ LSMT_BOOLEAN \
+ }
+
+#define LSM_CARRAY(type, name, qname) \
+ { \
+ qname, \
+ NULL, \
+ NULL, \
+ offsetof(type, name), \
+ sizeof (((type *)0)->name), \
+ 0, \
+ 0, \
+ LSMT_STRING_CHAR_ARRAY \
+ }
+
+#define LSM_STRING_PTR(type, name, qname) \
+ { \
+ qname, \
+ NULL, \
+ NULL, \
+ offsetof(type, name), \
+ sizeof (((type *)0)->name), \
+ 0, \
+ 0, \
+ LSMT_STRING_PTR \
+ }
+
+#define LSM_LIST(ptype, pname, ctype, cname, lejp_cb, cmap, qname) \
+ { \
+ qname, \
+ cmap, \
+ lejp_cb, \
+ offsetof(ptype, pname), \
+ sizeof (ctype), \
+ offsetof(ctype, cname), \
+ LWS_ARRAY_SIZE(cmap), \
+ LSMT_LIST \
+ }
+
+#define LSM_CHILD_PTR(ptype, pname, ctype, lejp_cb, cmap, qname) \
+ { \
+ qname, \
+ cmap, \
+ lejp_cb, \
+ offsetof(ptype, pname), \
+ sizeof (ctype), \
+ 0, \
+ LWS_ARRAY_SIZE(cmap), \
+ LSMT_CHILD_PTR \
+ }
+
+#define LSM_SCHEMA(ctype, lejp_cb, map, schema_name) \
+ { \
+ schema_name, \
+ map, \
+ lejp_cb, \
+ 0, \
+ sizeof (ctype), \
+ 0, \
+ LWS_ARRAY_SIZE(map), \
+ LSMT_SCHEMA \
+ }
+
+#define LSM_SCHEMA_DLL2(ctype, cdll2mem, lejp_cb, map, schema_name) \
+ { \
+ schema_name, \
+ map, \
+ lejp_cb, \
+ offsetof(ctype, cdll2mem), \
+ sizeof (ctype), \
+ 0, \
+ LWS_ARRAY_SIZE(map), \
+ LSMT_SCHEMA \
+ }
+
+typedef struct lws_struct_serialize_st {
+ const struct lws_dll2 *dllpos;
+ const lws_struct_map_t *map;
+ const char *obj;
+ size_t map_entries;
+ size_t map_entry;
+ size_t size;
+ char subsequent;
+ char idt;
+} lws_struct_serialize_st_t;
+
+enum {
+ LSSERJ_FLAG_PRETTY = 1
+};
+
+typedef struct lws_struct_serialize {
+ lws_struct_serialize_st_t st[LEJP_MAX_PARSING_STACK_DEPTH];
+
+ size_t offset;
+ size_t remaining;
+
+ int sp;
+ int flags;
+} lws_struct_serialize_t;
+
+typedef enum {
+ LSJS_RESULT_CONTINUE,
+ LSJS_RESULT_FINISH,
+ LSJS_RESULT_ERROR
+} lws_struct_json_serialize_result_t;
+
+LWS_VISIBLE LWS_EXTERN int
+lws_struct_json_init_parse(struct lejp_ctx *ctx, lejp_callback cb,
+ void *user);
+
+LWS_VISIBLE LWS_EXTERN signed char
+lws_struct_schema_only_lejp_cb(struct lejp_ctx *ctx, char reason);
+
+LWS_VISIBLE LWS_EXTERN signed char
+lws_struct_default_lejp_cb(struct lejp_ctx *ctx, char reason);
+
+LWS_VISIBLE LWS_EXTERN lws_struct_serialize_t *
+lws_struct_json_serialize_create(const lws_struct_map_t *map,
+ size_t map_entries, int flags,
+ const void *ptoplevel);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_struct_json_serialize_destroy(lws_struct_serialize_t **pjs);
+
+LWS_VISIBLE LWS_EXTERN lws_struct_json_serialize_result_t
+lws_struct_json_serialize(lws_struct_serialize_t *js, uint8_t *buf,
+ size_t len, size_t *written);
+
+#if defined(LWS_WITH_STRUCT_SQLITE3)
+
+LWS_VISIBLE LWS_EXTERN int
+lws_struct_sq3_serialize(sqlite3 *pdb, const lws_struct_map_t *schema,
+ lws_dll2_owner_t *owner, uint32_t manual_idx);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_struct_sq3_deserialize(sqlite3 *pdb, const char *filter, const char *order,
+ const lws_struct_map_t *schema, lws_dll2_owner_t *o,
+ struct lwsac **ac, int start, int limit);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_struct_sq3_create_table(sqlite3 *pdb, const lws_struct_map_t *schema);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_struct_sq3_open(struct lws_context *context, const char *sqlite3_path,
+ sqlite3 **pdb);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_struct_sq3_close(sqlite3 **pdb);
+
+#endif
--- /dev/null
+ /*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * This provides a clean way to interface lws user code to be able to
+ * work unchanged on different systems for fetching common system information,
+ * and performing common system operations like reboot.
+ */
+
+/*
+ * Types of system blob that can be set and retreived
+ */
+
+typedef enum {
+ LWS_SYSBLOB_TYPE_AUTH,
+ LWS_SYSBLOB_TYPE_CLIENT_CERT_DER = LWS_SYSBLOB_TYPE_AUTH + 2,
+ LWS_SYSBLOB_TYPE_CLIENT_KEY_DER,
+ LWS_SYSBLOB_TYPE_DEVICE_SERIAL,
+ LWS_SYSBLOB_TYPE_DEVICE_FW_VERSION,
+ LWS_SYSBLOB_TYPE_DEVICE_TYPE,
+ LWS_SYSBLOB_TYPE_NTP_SERVER,
+
+ LWS_SYSBLOB_TYPE_COUNT /* ... always last */
+} lws_system_blob_item_t;
+
+/* opaque generic blob whose content may be on-the-heap or pointed-to
+ * directly case by case. When it's on the heap, it can be produced by
+ * appending (it's a buflist underneath). Either way, it can be consumed by
+ * copying out a given length from a given offset.
+ */
+
+typedef struct lws_system_blob lws_system_blob_t;
+
+LWS_EXTERN LWS_VISIBLE void
+lws_system_blob_direct_set(lws_system_blob_t *b, const uint8_t *ptr, size_t len);
+
+LWS_EXTERN LWS_VISIBLE void
+lws_system_blob_heap_empty(lws_system_blob_t *b);
+
+LWS_EXTERN LWS_VISIBLE int
+lws_system_blob_heap_append(lws_system_blob_t *b, const uint8_t *ptr, size_t len);
+
+LWS_EXTERN LWS_VISIBLE size_t
+lws_system_blob_get_size(lws_system_blob_t *b);
+
+/* return 0 and sets *ptr to point to blob data if possible, nonzero = fail */
+LWS_EXTERN LWS_VISIBLE int
+lws_system_blob_get_single_ptr(lws_system_blob_t *b, const uint8_t **ptr);
+
+LWS_EXTERN LWS_VISIBLE int
+lws_system_blob_get(lws_system_blob_t *b, uint8_t *ptr, size_t *len, size_t ofs);
+
+LWS_EXTERN LWS_VISIBLE void
+lws_system_blob_destroy(lws_system_blob_t *b);
+
+/*
+ * Get the opaque blob for index idx of various system blobs. Returns 0 if
+ * *b was set otherwise nonzero means out of range
+ */
+
+LWS_EXTERN LWS_VISIBLE lws_system_blob_t *
+lws_system_get_blob(struct lws_context *context, lws_system_blob_item_t type,
+ int idx);
+
+/*
+ * Lws view of system state... normal operation from user code perspective is
+ * dependent on implicit (eg, knowing the date for cert validation) and
+ * explicit dependencies.
+ *
+ * Bit of lws and user code can register notification handlers that can enforce
+ * dependent operations before state transitions can complete.
+ */
+
+typedef enum { /* keep system_state_names[] in sync in context.c */
+ LWS_SYSTATE_UNKNOWN,
+
+ LWS_SYSTATE_CONTEXT_CREATED, /* context was just created */
+ LWS_SYSTATE_INITIALIZED, /* protocols initialized. Lws itself
+ * can operate normally */
+ LWS_SYSTATE_IFACE_COLDPLUG, /* existing net ifaces iterated */
+ LWS_SYSTATE_DHCP, /* at least one net iface configured */
+ LWS_SYSTATE_TIME_VALID, /* ntpclient ran, or hw time valid...
+ * tls cannot work until we reach here
+ */
+ LWS_SYSTATE_POLICY_VALID, /* user code knows how to operate... */
+ LWS_SYSTATE_REGISTERED, /* device has an identity... */
+ LWS_SYSTATE_AUTH1, /* identity used for main auth token */
+ LWS_SYSTATE_AUTH2, /* identity used for optional auth */
+
+ LWS_SYSTATE_OPERATIONAL, /* user code can operate normally */
+
+ LWS_SYSTATE_POLICY_INVALID, /* user code is changing its policies
+ * drop everything done with old
+ * policy, switch to new then enter
+ * LWS_SYSTATE_POLICY_VALID */
+} lws_system_states_t;
+
+
+typedef void (*lws_attach_cb_t)(struct lws_context *context, int tsi, void *opaque);
+struct lws_attach_item;
+
+typedef struct lws_system_ops {
+ int (*reboot)(void);
+ int (*set_clock)(lws_usec_t us);
+ int (*attach)(struct lws_context *context, int tsi, lws_attach_cb_t cb,
+ lws_system_states_t state, void *opaque,
+ struct lws_attach_item **get);
+ /**< if \p get is NULL, add an attach callback request to the pt for
+ * \p cb with arg \p opaque, that should be called when we're at or past
+ * system state \p state.
+ *
+ * If \p get is non-NULL, look for the first listed item on the pt whose
+ * state situation is ready, and set *get to point to it. If no items,
+ * or none where the system state is right, set *get to NULL.
+ *
+ * It's done like this so (*attach) can perform system-specific
+ * locking outside of lws core, for both getting and adding items the
+ * same so it is thread-safe. A non-threadsafe helper
+ * __lws_system_attach() is provided to do the actual work inside the
+ * system-specific locking.
+ */
+} lws_system_ops_t;
+
+/**
+ * lws_system_get_state_manager() - return the state mgr object for system state
+ *
+ * \param context: the lws_context
+ *
+ * The returned pointer can be used with the lws_state_ apis
+ */
+
+LWS_EXTERN LWS_VISIBLE lws_state_manager_t *
+lws_system_get_state_manager(struct lws_context *context);
+
+
+
+/* wrappers handle NULL members or no ops struct set at all cleanly */
+
+#define LWSSYSGAUTH_HEX (1 << 0)
+
+/**
+ * lws_system_get_ops() - get ahold of the system ops struct from the context
+ *
+ * \param context: the lws_context
+ *
+ * Returns the system ops struct. It may return NULL and if not, anything in
+ * there may be NULL.
+ */
+LWS_EXTERN LWS_VISIBLE const lws_system_ops_t *
+lws_system_get_ops(struct lws_context *context);
+
+/**
+ * lws_system_context_from_system_mgr() - return context from system state mgr
+ *
+ * \param mgr: pointer to specifically the system state mgr
+ *
+ * Returns the context from the system state mgr. Helper since the lws_context
+ * is opaque.
+ */
+LWS_EXTERN LWS_VISIBLE struct lws_context *
+lws_system_context_from_system_mgr(lws_state_manager_t *mgr);
+
+
+/**
+ * __lws_system_attach() - get and set items on context attach list
+ *
+ * \param context: context to get or set attach items to
+ * \param tsi: thread service index (normally 0)
+ * \param cb: callback to call from context event loop thread
+ * \param state: the lws_system state we have to be in or have passed through
+ * \param opaque: optional pointer to user specific info given to callback
+ * \param get: NULL, or pointer to pointer to take detached tail item on exit
+ *
+ * This allows other threads to enqueue callback requests to happen from a pt's
+ * event loop thread safely. The callback gets the context pointer and a user
+ * opaque pointer that can be optionally given when the item is added to the
+ * attach list.
+ *
+ * This api is the no-locking core function for getting and setting items on the
+ * pt's attach list. The lws_system operation (*attach) is the actual
+ * api that user and internal code calls for this feature, it should perform
+ * system-specific locking, call this helper, release the locking and then
+ * return the result. This api is public only so it can be used in the locked
+ * implementation of (*attach).
+ *
+ * If get is NULL, then the call adds to the head of the pt attach list using
+ * cb, state, and opaque; if get is non-NULL, then *get is set to the first
+ * waiting attached item that meets the state criteria and that item is removed
+ * from the list.
+ *
+ * This is a non-threadsafe helper only designed to be called from
+ * implementations of struct lws_system's (*attach) operation where system-
+ * specific locking has been applied around it, making it threadsafe.
+ */
+LWS_EXTERN LWS_VISIBLE int
+__lws_system_attach(struct lws_context *context, int tsi, lws_attach_cb_t cb,
+ lws_system_states_t state, void *opaque,
+ struct lws_attach_item **get);
+
+
+typedef int (*dhcpc_cb_t)(void *opaque, int af, uint8_t *ip, int ip_len);
+
+/**
+ * lws_dhcpc_request() - add a network interface to dhcpc management
+ *
+ * \param c: the lws_context
+ * \param i: the interface name, like "eth0"
+ * \param af: address family
+ * \param cb: the change callback
+ * \param opaque: opaque pointer given to the callback
+ *
+ * Register a network interface as being managed by DHCP. lws will proceed to
+ * try to acquire an IP. Requires LWS_WITH_SYS_DHCP_CLIENT at cmake.
+ */
+int
+lws_dhcpc_request(struct lws_context *c, const char *i, int af, dhcpc_cb_t cb,
+ void *opaque);
+
+/**
+ * lws_dhcpc_remove() - remove a network interface to dhcpc management
+ *
+ * \param context: the lws_context
+ * \param iface: the interface name, like "eth0"
+ *
+ * Remove handling of the network interface from dhcp.
+ */
+int
+lws_dhcpc_remove(struct lws_context *context, const char *iface);
+
+/**
+ * lws_dhcpc_status() - has any interface reached BOUND state
+ *
+ * \param context: the lws_context
+ * \param sa46: set to a DNS server from a bound interface, or NULL
+ *
+ * Returns 1 if any network interface managed by dhcpc has reached the BOUND
+ * state (has acquired an IP, gateway and DNS server), otherwise 0.
+ */
+int
+lws_dhcpc_status(struct lws_context *context, lws_sockaddr46 *sa46);
--- /dev/null
+ /*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * lws_test_sequencer manages running an array of unit tests.
+ */
+
+typedef void (*lws_test_sequence_cb)(const void *cb_user);
+
+typedef struct lws_test_sequencer_args {
+ lws_abs_t *abs; /* abstract protocol + unit test txport */
+ lws_unit_test_t *tests; /* array of lws_unit_test_t */
+ int *results; /* takes result dispositions */
+ int results_max; /* max space usable in results */
+ int *count_tests; /* count of done tests */
+ int *count_passes; /* count of passed tests */
+ lws_test_sequence_cb cb; /* completion callback */
+ void *cb_user; /* opaque user ptr given to cb */
+} lws_test_sequencer_args_t;
+
+/**
+ * lws_abs_unit_test_sequencer() - helper to sequence multiple unit tests
+ *
+ * \param args: lws_test_sequencer_args_t prepared with arguments for the tests
+ *
+ * This helper sequences one or more unit tests to run and collects the results.
+ *
+ * The incoming abs should be set up for the abstract protocol you want to test
+ * and the lws unit-test transport.
+ *
+ * Results are one of
+ *
+ * LPE_SUCCEEDED
+ * LPE_FAILED
+ * LPE_FAILED_UNEXPECTED_TIMEOUT
+ * LPE_FAILED_UNEXPECTED_PASS
+ * LPE_FAILED_UNEXPECTED_CLOSE
+ *
+ * The callback args->cb is called when the tests have been done.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_abs_unit_test_sequencer(const lws_test_sequencer_args_t *args);
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/** \defgroup threadpool Threadpool related functions
+ * ##Threadpool
+ * \ingroup lwsapi
+ *
+ * This allows you to create one or more pool of threads which can run tasks
+ * associated with a wsi. If the pool is busy, tasks wait on a queue.
+ *
+ * Tasks don't have to be atomic, if they will take more than a few tens of ms
+ * they should return back to the threadpool worker with a return of 0. This
+ * will allow them to abort cleanly.
+ */
+//@{
+
+struct lws_threadpool;
+struct lws_threadpool_task;
+
+enum lws_threadpool_task_status {
+ LWS_TP_STATUS_QUEUED,
+ LWS_TP_STATUS_RUNNING,
+ LWS_TP_STATUS_SYNCING,
+ LWS_TP_STATUS_STOPPING,
+ LWS_TP_STATUS_FINISHED, /* lws_threadpool_task_status() frees task */
+ LWS_TP_STATUS_STOPPED, /* lws_threadpool_task_status() frees task */
+};
+
+enum lws_threadpool_task_return {
+ /** Still work to do, just confirming not being stopped */
+ LWS_TP_RETURN_CHECKING_IN,
+ /** Still work to do, enter cond_wait until service thread syncs. This
+ * is used if you have filled your buffer(s) of data to the service
+ * thread and are blocked until the service thread completes sending at
+ * least one.
+ */
+ LWS_TP_RETURN_SYNC,
+ /** No more work to do... */
+ LWS_TP_RETURN_FINISHED,
+ /** Responding to request to stop */
+ LWS_TP_RETURN_STOPPED,
+
+ /* OR on to indicate this task wishes to outlive its wsi */
+ LWS_TP_RETURN_FLAG_OUTLIVE = 64
+};
+
+struct lws_threadpool_create_args {
+ int threads;
+ int max_queue_depth;
+};
+
+struct lws_threadpool_task_args {
+ struct lws *wsi; /**< user must set to wsi task is bound to */
+ void *user; /**< user may set (user-private pointer) */
+ const char *name; /**< user may set to describe task */
+ char async_task; /**< set to allow the task to shrug off the loss
+ of the associated wsi and continue to
+ completion */
+ enum lws_threadpool_task_return (*task)(void *user,
+ enum lws_threadpool_task_status s);
+ /**< user must set to actual task function */
+ void (*cleanup)(struct lws *wsi, void *user);
+ /**< socket lifecycle may end while task is not stoppable, so the task
+ * must be able to detach from any wsi and clean itself up when it does
+ * stop. If NULL, no cleanup necessary, otherwise point to a user-
+ * supplied function that destroys the stuff in \p user.
+ *
+ * wsi may be NULL on entry, indicating the task got detached due to the
+ * wsi closing before.
+ */
+};
+
+/**
+ * lws_threadpool_create() - create a pool of worker threads
+ *
+ * \param context: the lws_context the threadpool will exist inside
+ * \param args: argument struct prepared by caller
+ * \param format: printf-type format for the task name
+ * \param ...: printf type args for the task name format
+ *
+ * Creates a pool of worker threads with \p threads and a queue of up to
+ * \p max_queue_depth waiting tasks if all the threads are busy.
+ *
+ * Returns NULL if OOM, or a struct lws_threadpool pointer that must be
+ * destroyed by lws_threadpool_destroy().
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_threadpool *
+lws_threadpool_create(struct lws_context *context,
+ const struct lws_threadpool_create_args *args,
+ const char *format, ...) LWS_FORMAT(3);
+
+/**
+ * lws_threadpool_finish() - Stop all pending and running tasks
+ *
+ * \param tp: the threadpool object
+ *
+ * Marks the threadpool as under destruction. Removes everything from the
+ * pending queue and completes those tasks as LWS_TP_STATUS_STOPPED.
+ *
+ * Running tasks will also get LWS_TP_STATUS_STOPPED as soon as they
+ * "resurface".
+ *
+ * This doesn't reap tasks or free the threadpool, the reaping is done by the
+ * lws_threadpool_task_status() on the done task.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_threadpool_finish(struct lws_threadpool *tp);
+
+/**
+ * lws_threadpool_destroy() - Destroy a threadpool
+ *
+ * \param tp: the threadpool object
+ *
+ * Waits for all worker threads to stop, ends the threads and frees the tp.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_threadpool_destroy(struct lws_threadpool *tp);
+
+/**
+ * lws_threadpool_enqueue() - Queue the task and run it on a worker thread when possible
+ *
+ * \param tp: the threadpool to queue / run on
+ * \param args: information about what to run
+ * \param format: printf-type format for the task name
+ * \param ...: printf type args for the task name format
+ *
+ * This asks for a task to run ASAP on a worker thread in threadpool \p tp.
+ *
+ * The args defines the wsi, a user-private pointer, a timeout in secs and
+ * a pointer to the task function.
+ *
+ * Returns NULL or an opaque pointer to the queued (or running, or completed)
+ * task.
+ *
+ * Once a task is created and enqueued, it can only be destroyed by calling
+ * lws_threadpool_task_status() on it after it has reached the state
+ * LWS_TP_STATUS_FINISHED or LWS_TP_STATUS_STOPPED.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_threadpool_task *
+lws_threadpool_enqueue(struct lws_threadpool *tp,
+ const struct lws_threadpool_task_args *args,
+ const char *format, ...) LWS_FORMAT(3);
+
+/**
+ * lws_threadpool_dequeue() - Dequeue or try to stop a running task
+ *
+ * \param wsi: the wsi whose current task we want to eliminate
+ *
+ * Returns 0 is the task was dequeued or already compeleted, or 1 if the task
+ * has been asked to stop asynchronously.
+ *
+ * This doesn't free the task. It only shortcuts it to state
+ * LWS_TP_STATUS_STOPPED. lws_threadpool_task_status() must be performed on
+ * the task separately once it is in LWS_TP_STATUS_STOPPED to free the task.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_threadpool_dequeue(struct lws *wsi);
+
+/**
+ * lws_threadpool_task_status() - Dequeue or try to stop a running task
+ *
+ * \param wsi: the wsi to query the current task of
+ * \param task: receives a pointer to the opaque task
+ * \param user: receives a void * pointer to the task user data
+ *
+ * This is the equivalent of posix waitpid()... it returns the status of the
+ * task, and if the task is in state LWS_TP_STATUS_FINISHED or
+ * LWS_TP_STATUS_STOPPED, frees \p task. If in another state, the task
+ * continues to exist.
+ *
+ * This is designed to be called from the service thread.
+ *
+ * Its use is to make sure the service thread has seen the state of the task
+ * before deleting it.
+ */
+LWS_VISIBLE LWS_EXTERN enum lws_threadpool_task_status
+lws_threadpool_task_status_wsi(struct lws *wsi,
+ struct lws_threadpool_task **task, void **user);
+
+/**
+ * lws_threadpool_task_sync() - Indicate to a stalled task it may continue
+ *
+ * \param task: the task to unblock
+ * \param stop: 0 = run after unblock, 1 = when he unblocks, stop him
+ *
+ * Inform the task that the service thread has finished with the shared data
+ * and that the task, if blocked in LWS_TP_RETURN_SYNC, may continue.
+ *
+ * If the lws service context determined that the task must be aborted, it
+ * should still call this but with stop = 1, causing the task to finish.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_threadpool_task_sync(struct lws_threadpool_task *task, int stop);
+
+/**
+ * lws_threadpool_dump() - dump the state of a threadpool to the log
+ *
+ * \param tp: The threadpool to dump
+ *
+ * This locks the threadpool and then dumps the pending queue, the worker
+ * threads and the done queue, together with time information for how long
+ * the tasks have been in their current state, how long they have occupied a
+ * thread, etc.
+ *
+ * This only does anything on lws builds with CMAKE_BUILD_TYPE=DEBUG, otherwise
+ * while it still exists, it's a NOP.
+ */
+
+LWS_VISIBLE LWS_EXTERN void
+lws_threadpool_dump(struct lws_threadpool *tp);
+//@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*! \defgroup timeout Connection timeouts
+
+ APIs related to setting connection timeouts
+*/
+//@{
+
+/*
+ * NOTE: These public enums are part of the abi. If you want to add one,
+ * add it at where specified so existing users are unaffected.
+ */
+enum pending_timeout {
+ NO_PENDING_TIMEOUT = 0,
+ PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE = 1,
+ PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE = 2,
+ PENDING_TIMEOUT_ESTABLISH_WITH_SERVER = 3,
+ PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE = 4,
+ PENDING_TIMEOUT_AWAITING_PING = 5,
+ PENDING_TIMEOUT_CLOSE_ACK = 6,
+ PENDING_TIMEOUT_UNUSED1 = 7,
+ PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE = 8,
+ PENDING_TIMEOUT_SSL_ACCEPT = 9,
+ PENDING_TIMEOUT_HTTP_CONTENT = 10,
+ PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND = 11,
+ PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE = 12,
+ PENDING_TIMEOUT_SHUTDOWN_FLUSH = 13,
+ PENDING_TIMEOUT_CGI = 14,
+ PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE = 15,
+ PENDING_TIMEOUT_WS_PONG_CHECK_SEND_PING = 16,
+ PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG = 17,
+ PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD = 18,
+ PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY = 19,
+ PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY = 20,
+ PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY = 21,
+ PENDING_TIMEOUT_KILLED_BY_SSL_INFO = 22,
+ PENDING_TIMEOUT_KILLED_BY_PARENT = 23,
+ PENDING_TIMEOUT_CLOSE_SEND = 24,
+ PENDING_TIMEOUT_HOLDING_AH = 25,
+ PENDING_TIMEOUT_UDP_IDLE = 26,
+ PENDING_TIMEOUT_CLIENT_CONN_IDLE = 27,
+ PENDING_TIMEOUT_LAGGING = 28,
+ PENDING_TIMEOUT_THREADPOOL = 29,
+ PENDING_TIMEOUT_THREADPOOL_TASK = 30,
+ PENDING_TIMEOUT_KILLED_BY_PROXY_CLIENT_CLOSE = 31,
+ PENDING_TIMEOUT_USER_OK = 32,
+
+ /****** add new things just above ---^ ******/
+
+ PENDING_TIMEOUT_USER_REASON_BASE = 1000
+};
+
+#define lws_time_in_microseconds lws_now_usecs
+
+#define LWS_TO_KILL_ASYNC -1
+/**< If LWS_TO_KILL_ASYNC is given as the timeout sec in a lws_set_timeout()
+ * call, then the connection is marked to be killed at the next timeout
+ * check. This is how you should force-close the wsi being serviced if
+ * you are doing it outside the callback (where you should close by nonzero
+ * return).
+ */
+#define LWS_TO_KILL_SYNC -2
+/**< If LWS_TO_KILL_SYNC is given as the timeout sec in a lws_set_timeout()
+ * call, then the connection is closed before returning (which may delete
+ * the wsi). This should only be used where the wsi being closed is not the
+ * wsi currently being serviced.
+ */
+/**
+ * lws_set_timeout() - marks the wsi as subject to a timeout some seconds hence
+ *
+ * \param wsi: Websocket connection instance
+ * \param reason: timeout reason
+ * \param secs: how many seconds. You may set to LWS_TO_KILL_ASYNC to
+ * force the connection to timeout at the next opportunity, or
+ * LWS_TO_KILL_SYNC to close it synchronously if you know the
+ * wsi is not the one currently being serviced.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs);
+
+/**
+ * lws_set_timeout_us() - marks the wsi as subject to a timeout some us hence
+ *
+ * \param wsi: Websocket connection instance
+ * \param reason: timeout reason
+ * \param us: 0 removes the timeout, otherwise number of us to wait
+ *
+ * Higher-resolution version of lws_set_timeout(). Actual resolution depends
+ * on platform and load, usually ms.
+ */
+void
+lws_set_timeout_us(struct lws *wsi, enum pending_timeout reason, lws_usec_t us);
+
+#define LWS_SET_TIMER_USEC_CANCEL ((lws_usec_t)-1ll)
+#define LWS_USEC_PER_SEC ((lws_usec_t)1000000)
+
+/**
+ * lws_set_timer_usecs() - schedules a callback on the wsi in the future
+ *
+ * \param wsi: Websocket connection instance
+ * \param usecs: LWS_SET_TIMER_USEC_CANCEL removes any existing scheduled
+ * callback, otherwise number of microseconds in the future
+ * the callback will occur at.
+ *
+ * NOTE: event loop support for this:
+ *
+ * default poll() loop: yes
+ * libuv event loop: yes
+ * libev: not implemented (patch welcome)
+ * libevent: not implemented (patch welcome)
+ *
+ * After the deadline expires, the wsi will get a callback of type
+ * LWS_CALLBACK_TIMER and the timer is exhausted. The deadline may be
+ * continuously deferred by further calls to lws_set_timer_usecs() with a later
+ * deadline, or cancelled by lws_set_timer_usecs(wsi, -1).
+ *
+ * If the timer should repeat, lws_set_timer_usecs() must be called again from
+ * LWS_CALLBACK_TIMER.
+ *
+ * Accuracy depends on the platform and the load on the event loop or system...
+ * all that's guaranteed is the callback will come after the requested wait
+ * period.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_set_timer_usecs(struct lws *wsi, lws_usec_t usecs);
+
+/*
+ * lws_timed_callback_vh_protocol() - calls back a protocol on a vhost after
+ * the specified delay in seconds
+ *
+ * \param vh: the vhost to call back
+ * \param protocol: the protocol to call back
+ * \param reason: callback reason
+ * \param secs: how many seconds in the future to do the callback.
+ *
+ * Callback the specified protocol with a fake wsi pointing to the specified
+ * vhost and protocol, with the specified reason, at the specified time in the
+ * future.
+ *
+ * Returns 0 if OK or 1 on OOM.
+ *
+ * In the multithreaded service case, the callback will occur in the same
+ * service thread context as the call to this api that requested it. If it is
+ * called from a non-service thread, tsi 0 will handle it.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_timed_callback_vh_protocol(struct lws_vhost *vh,
+ const struct lws_protocols *prot,
+ int reason, int secs);
+
+/*
+ * lws_timed_callback_vh_protocol_us() - calls back a protocol on a vhost after
+ * the specified delay in us
+ *
+ * \param vh: the vhost to call back
+ * \param protocol: the protocol to call back
+ * \param reason: callback reason
+ * \param us: how many us in the future to do the callback.
+ *
+ * Callback the specified protocol with a fake wsi pointing to the specified
+ * vhost and protocol, with the specified reason, at the specified time in the
+ * future.
+ *
+ * Returns 0 if OK or 1 on OOM.
+ *
+ * In the multithreaded service case, the callback will occur in the same
+ * service thread context as the call to this api that requested it. If it is
+ * called from a non-service thread, tsi 0 will handle it.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_timed_callback_vh_protocol_us(struct lws_vhost *vh,
+ const struct lws_protocols *prot, int reason,
+ lws_usec_t us);
+
+struct lws_sorted_usec_list;
+
+typedef void (*sul_cb_t)(struct lws_sorted_usec_list *sul);
+
+typedef struct lws_sorted_usec_list {
+ struct lws_dll2 list; /* simplify the code by keeping this at start */
+ sul_cb_t cb;
+ lws_usec_t us;
+} lws_sorted_usec_list_t;
+
+
+/*
+ * lws_sul_schedule() - schedule a callback
+ *
+ * \param context: the lws_context
+ * \param tsi: the thread service index (usually 0)
+ * \param sul: pointer to the sul element
+ * \param cb: the scheduled callback
+ * \param us: the delay before the callback arrives, or
+ * LWS_SET_TIMER_USEC_CANCEL to cancel it.
+ *
+ * Generic callback-at-a-later time function. The callback happens on the
+ * event loop thread context.
+ *
+ * Although the api has us resultion, the actual resolution depends on the
+ * platform and is commonly 1ms.
+ *
+ * This doesn't allocate and doesn't fail.
+ *
+ * You can call it again with another us value to change the delay.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_sul_schedule(struct lws_context *context, int tsi,
+ lws_sorted_usec_list_t *sul, sul_cb_t cb, lws_usec_t us);
+
+/*
+ * lws_validity_confirmed() - reset the validity timer for a network connection
+ *
+ * \param wsi: the connection that saw traffic proving the connection valid
+ *
+ * Network connections are subject to intervals defined by the context, the
+ * vhost if server connections, or the client connect info if a client
+ * connection. If the connection goes longer than the specified time since
+ * last observing traffic that can only happen if traffic is passing in both
+ * directions, then lws will try to create a PING transaction on the network
+ * connection.
+ *
+ * If the connection reaches the specified `.secs_since_valid_hangup` time
+ * still without any proof of validity, the connection will be closed.
+ *
+ * If the PONG comes, or user code observes traffic that satisfies the proof
+ * that both directions are passing traffic to the peer and calls this api,
+ * the connection validity timer is reset and the scheme repeats.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_validity_confirmed(struct lws *wsi);
+
+/*
+ * These are not normally needed, they're exported for the case there's code
+ * using lws_sul for which lws is an optional link dependency.
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+__lws_sul_insert(lws_dll2_owner_t *own, lws_sorted_usec_list_t *sul,
+ lws_usec_t us);
+
+LWS_VISIBLE LWS_EXTERN lws_usec_t
+__lws_sul_service_ripe(lws_dll2_owner_t *own, lws_usec_t usnow);
+
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/* Do not treat - as a terminal character, so "my-token" is one token */
+#define LWS_TOKENIZE_F_MINUS_NONTERM (1 << 0)
+/* Separately report aggregate colon-delimited tokens */
+#define LWS_TOKENIZE_F_AGG_COLON (1 << 1)
+/* Enforce sequencing for a simple token , token , token ... list */
+#define LWS_TOKENIZE_F_COMMA_SEP_LIST (1 << 2)
+/* Allow more characters in the tokens and less delimiters... default is
+ * only alphanumeric + underscore in tokens */
+#define LWS_TOKENIZE_F_RFC7230_DELIMS (1 << 3)
+/* Do not treat . as a terminal character, so "warmcat.com" is one token */
+#define LWS_TOKENIZE_F_DOT_NONTERM (1 << 4)
+/* If something starts looking like a float, like 1.2, force to be string token.
+ * This lets you receive dotted-quads like 192.168.0.1 as string tokens, and
+ * avoids illegal float format detection like 1.myserver.com */
+#define LWS_TOKENIZE_F_NO_FLOATS (1 << 5)
+/* Instead of LWS_TOKZE_INTEGER, report integers as any other string token */
+#define LWS_TOKENIZE_F_NO_INTEGERS (1 << 6)
+/* # makes the rest of the line a comment */
+#define LWS_TOKENIZE_F_HASH_COMMENT (1 << 7)
+/* Do not treat / as a terminal character, so "multipart/related" is one token */
+#define LWS_TOKENIZE_F_SLASH_NONTERM (1 << 8)
+
+typedef enum {
+
+ LWS_TOKZE_ERRS = 5, /* the number of errors defined */
+
+ LWS_TOKZE_ERR_BROKEN_UTF8 = -5, /* malformed or partial utf8 */
+ LWS_TOKZE_ERR_UNTERM_STRING = -4, /* ended while we were in "" */
+ LWS_TOKZE_ERR_MALFORMED_FLOAT = -3, /* like 0..1 or 0.1.1 */
+ LWS_TOKZE_ERR_NUM_ON_LHS = -2, /* like 123= or 0.1= */
+ LWS_TOKZE_ERR_COMMA_LIST = -1, /* like ",tok", or, "tok,," */
+
+ LWS_TOKZE_ENDED = 0, /* no more content */
+
+ /* Note: results have ordinal 1+, EOT is 0 and errors are < 0 */
+
+ LWS_TOKZE_DELIMITER, /* a delimiter appeared */
+ LWS_TOKZE_TOKEN, /* a token appeared */
+ LWS_TOKZE_INTEGER, /* an integer appeared */
+ LWS_TOKZE_FLOAT, /* a float appeared */
+ LWS_TOKZE_TOKEN_NAME_EQUALS, /* token [whitespace] = */
+ LWS_TOKZE_TOKEN_NAME_COLON, /* token [whitespace] : (only with
+ LWS_TOKENIZE_F_AGG_COLON flag) */
+ LWS_TOKZE_QUOTED_STRING, /* "*", where * may have any char */
+
+} lws_tokenize_elem;
+
+/*
+ * helper enums to allow caller to enforce legal delimiter sequencing, eg
+ * disallow "token,,token", "token,", and ",token"
+ */
+
+enum lws_tokenize_delimiter_tracking {
+ LWSTZ_DT_NEED_FIRST_CONTENT,
+ LWSTZ_DT_NEED_DELIM,
+ LWSTZ_DT_NEED_NEXT_CONTENT,
+};
+
+typedef struct lws_tokenize {
+ const char *start; /**< set to the start of the string to tokenize */
+ const char *token; /**< the start of an identified token or delimiter */
+ size_t len; /**< set to the length of the string to tokenize */
+ size_t token_len; /**< the length of the identied token or delimiter */
+
+ uint16_t flags; /**< optional LWS_TOKENIZE_F_ flags, or 0 */
+ uint8_t delim;
+
+ int8_t e; /**< convenient for storing lws_tokenize return */
+} lws_tokenize_t;
+
+/**
+ * lws_tokenize() - breaks down a string into tokens and delimiters in-place
+ *
+ * \param ts: the lws_tokenize struct to init
+ * \param start: the string to tokenize
+ * \param flags: LWS_TOKENIZE_F_ option flags
+ *
+ * This initializes the tokenize struct to point to the given string, and
+ * sets the length to 2GiB - 1 (so there must be a terminating NUL)... you can
+ * override this requirement by setting ts.len yourself before using it.
+ *
+ * .delim is also initialized to LWSTZ_DT_NEED_FIRST_CONTENT.
+ */
+
+LWS_VISIBLE LWS_EXTERN void
+lws_tokenize_init(struct lws_tokenize *ts, const char *start, int flags);
+
+/**
+ * lws_tokenize() - breaks down a string into tokens and delimiters in-place
+ *
+ * \param ts: the lws_tokenize struct with information and state on what to do
+ *
+ * The \p ts struct should have its start, len and flags members initialized to
+ * reflect the string to be tokenized and any options.
+ *
+ * Then `lws_tokenize()` may be called repeatedly on the struct, returning one
+ * of `lws_tokenize_elem` each time, and with the struct's `token` and
+ * `token_len` members set to describe the content of the delimiter or token
+ * payload each time.
+ *
+ * There are no allocations during the process.
+ *
+ * returns lws_tokenize_elem that was identified (LWS_TOKZE_ENDED means reached
+ * the end of the string).
+ */
+
+LWS_VISIBLE LWS_EXTERN lws_tokenize_elem
+lws_tokenize(struct lws_tokenize *ts);
+
+/**
+ * lws_tokenize_cstr() - copy token string to NUL-terminated buffer
+ *
+ * \param ts: pointer to lws_tokenize struct to operate on
+ * \param str: destination buffer
+ * \pparam max: bytes in destination buffer
+ *
+ * returns 0 if OK or nonzero if the string + NUL won't fit.
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_tokenize_cstr(struct lws_tokenize *ts, char *str, size_t max);
+
+
+/*
+ * lws_strexp: flexible string expansion helper api
+ *
+ * This stateful helper can handle multiple separate input chunks and multiple
+ * output buffer loads with arbitrary boundaries between literals and expanded
+ * symbols. This allows it to handle fragmented input as well as arbitrarily
+ * long symbol expansions that are bigger than the output buffer itself.
+ *
+ * A user callback is used to convert symbol names to the symbol value.
+ *
+ * A single byte buffer for input and another for output can process any
+ * length substitution then. The state object is around 64 bytes on a 64-bit
+ * system and it only uses 8 bytes stack.
+ */
+
+
+typedef int (*lws_strexp_expand_cb)(void *priv, const char *name, char *out,
+ size_t *pos, size_t olen, size_t *exp_ofs);
+
+typedef struct lws_strexp {
+ char name[32];
+ lws_strexp_expand_cb cb;
+ void *priv;
+ char *out;
+ size_t olen;
+ size_t pos;
+
+ size_t exp_ofs;
+
+ uint8_t name_pos;
+ char state;
+} lws_strexp_t;
+
+enum {
+ LSTRX_DONE, /* it completed OK */
+ LSTRX_FILLED_OUT, /* out buf filled and needs resetting */
+ LSTRX_FATAL_NAME_TOO_LONG = -1, /* fatal */
+ LSTRX_FATAL_NAME_UNKNOWN = -2,
+};
+
+
+/**
+ * lws_strexp_init() - initialize an lws_strexp_t for use
+ *
+ * \p exp: the exp object to init
+ * \p priv: the user's object pointer to pass to callback
+ * \p cb: the callback to expand named objects
+ * \p out: the start of the output buffer
+ * \p olen: the length of the output buffer in bytes
+ *
+ * Prepares an lws_strexp_t for use and sets the initial output buffer
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_strexp_init(lws_strexp_t *exp, void *priv, lws_strexp_expand_cb cb,
+ char *out, size_t olen);
+
+/**
+ * lws_strexp_reset_out() - reset the output buffer on an existing strexp
+ *
+ * \p exp: the exp object to init
+ * \p out: the start of the output buffer
+ * \p olen: the length of the output buffer in bytes
+ *
+ * Provides a new output buffer for lws_strexp_expand() to continue to write
+ * into. It can be the same as the old one if it has been copied out or used.
+ * The position of the next write will be reset to the start of the given buf.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_strexp_reset_out(lws_strexp_t *exp, char *out, size_t olen);
+
+/**
+ * lws_strexp_expand() - copy / expand a string into the output buffer
+ *
+ * \p exp: the exp object for the copy / expansion
+ * \p in: the start of the next input data
+ * \p len: the length of the input data
+ * \p pused_in: pointer to write the amount of input used
+ * \p pused_out: pointer to write the amount of output used
+ *
+ * Copies in to the output buffer set in exp, expanding any ${name} tokens using
+ * the callback. \p *pused_in is set to the number of input chars used and
+ * \p *pused_out the number of output characters used
+ *
+ * May return LSTRX_FILLED_OUT early with *pused < len if the output buffer is
+ * filled. Handle the output buffer and reset it with lws_strexp_reset_out()
+ * before calling again with adjusted in / len to continue.
+ *
+ * In the case of large expansions, the expansion itself may fill the output
+ * buffer, in which case the expansion callback returns the LSTRX_FILLED_OUT
+ * and will be called again to continue with its *exp_ofs parameter set
+ * appropriately.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_strexp_expand(lws_strexp_t *exp, const char *in, size_t len,
+ size_t *pused_in, size_t *pused_out);
+
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*! \defgroup fops file operation wrapping
+ *
+ * ##File operation wrapping
+ *
+ * Use these helper functions if you want to access a file from the perspective
+ * of a specific wsi, which is usually the case. If you just want contextless
+ * file access, use the fops callbacks directly with NULL wsi instead of these
+ * helpers.
+ *
+ * If so, then it calls the platform handler or user overrides where present
+ * (as defined in info->fops)
+ *
+ * The advantage from all this is user code can be portable for file operations
+ * without having to deal with differences between platforms.
+ */
+//@{
+
+/** struct lws_plat_file_ops - Platform-specific file operations
+ *
+ * These provide platform-agnostic ways to deal with filesystem access in the
+ * library and in the user code.
+ */
+
+#if defined(LWS_PLAT_FREERTOS)
+/* sdk preprocessor defs? compiler issue? gets confused with member names */
+#define LWS_FOP_OPEN _open
+#define LWS_FOP_CLOSE _close
+#define LWS_FOP_SEEK_CUR _seek_cur
+#define LWS_FOP_READ _read
+#define LWS_FOP_WRITE _write
+#else
+#define LWS_FOP_OPEN open
+#define LWS_FOP_CLOSE close
+#define LWS_FOP_SEEK_CUR seek_cur
+#define LWS_FOP_READ read
+#define LWS_FOP_WRITE write
+#endif
+
+#define LWS_FOP_FLAGS_MASK ((1 << 23) - 1)
+#define LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP (1 << 24)
+#define LWS_FOP_FLAG_COMPR_IS_GZIP (1 << 25)
+#define LWS_FOP_FLAG_MOD_TIME_VALID (1 << 26)
+#define LWS_FOP_FLAG_VIRTUAL (1 << 27)
+
+struct lws_plat_file_ops;
+
+struct lws_fop_fd {
+ lws_filefd_type fd;
+ /**< real file descriptor related to the file... */
+ const struct lws_plat_file_ops *fops;
+ /**< fops that apply to this fop_fd */
+ void *filesystem_priv;
+ /**< ignored by lws; owned by the fops handlers */
+ lws_filepos_t pos;
+ /**< generic "position in file" */
+ lws_filepos_t len;
+ /**< generic "length of file" */
+ lws_fop_flags_t flags;
+ /**< copy of the returned flags */
+ uint32_t mod_time;
+ /**< optional "modification time of file", only valid if .open()
+ * set the LWS_FOP_FLAG_MOD_TIME_VALID flag */
+};
+typedef struct lws_fop_fd *lws_fop_fd_t;
+
+struct lws_fops_index {
+ const char *sig; /* NULL or vfs signature, eg, ".zip/" */
+ uint8_t len; /* length of above string */
+};
+
+struct lws_plat_file_ops {
+ lws_fop_fd_t (*LWS_FOP_OPEN)(const struct lws_plat_file_ops *fops,
+ const char *filename, const char *vpath,
+ lws_fop_flags_t *flags);
+ /**< Open file (always binary access if plat supports it)
+ * vpath may be NULL, or if the fops understands it, the point at which
+ * the filename's virtual part starts.
+ * *flags & LWS_FOP_FLAGS_MASK should be set to O_RDONLY or O_RDWR.
+ * If the file may be gzip-compressed,
+ * LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP is set. If it actually is
+ * gzip-compressed, then the open handler should OR
+ * LWS_FOP_FLAG_COMPR_IS_GZIP on to *flags before returning.
+ */
+ int (*LWS_FOP_CLOSE)(lws_fop_fd_t *fop_fd);
+ /**< close file AND set the pointer to NULL */
+ lws_fileofs_t (*LWS_FOP_SEEK_CUR)(lws_fop_fd_t fop_fd,
+ lws_fileofs_t offset_from_cur_pos);
+ /**< seek from current position */
+ int (*LWS_FOP_READ)(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
+ uint8_t *buf, lws_filepos_t len);
+ /**< Read from file, on exit *amount is set to amount actually read */
+ int (*LWS_FOP_WRITE)(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
+ uint8_t *buf, lws_filepos_t len);
+ /**< Write to file, on exit *amount is set to amount actually written */
+
+ struct lws_fops_index fi[3];
+ /**< vfs path signatures implying use of this fops */
+
+ const struct lws_plat_file_ops *next;
+ /**< NULL or next fops in list */
+
+ /* Add new things just above here ---^
+ * This is part of the ABI, don't needlessly break compatibility */
+};
+
+/**
+ * lws_get_fops() - get current file ops
+ *
+ * \param context: context
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_plat_file_ops * LWS_WARN_UNUSED_RESULT
+lws_get_fops(struct lws_context *context);
+LWS_VISIBLE LWS_EXTERN void
+lws_set_fops(struct lws_context *context, const struct lws_plat_file_ops *fops);
+/**
+ * lws_vfs_tell() - get current file position
+ *
+ * \param fop_fd: fop_fd we are asking about
+ */
+LWS_VISIBLE LWS_EXTERN lws_filepos_t LWS_WARN_UNUSED_RESULT
+lws_vfs_tell(lws_fop_fd_t fop_fd);
+/**
+ * lws_vfs_get_length() - get current file total length in bytes
+ *
+ * \param fop_fd: fop_fd we are asking about
+ */
+LWS_VISIBLE LWS_EXTERN lws_filepos_t LWS_WARN_UNUSED_RESULT
+lws_vfs_get_length(lws_fop_fd_t fop_fd);
+/**
+ * lws_vfs_get_mod_time() - get time file last modified
+ *
+ * \param fop_fd: fop_fd we are asking about
+ */
+LWS_VISIBLE LWS_EXTERN uint32_t LWS_WARN_UNUSED_RESULT
+lws_vfs_get_mod_time(lws_fop_fd_t fop_fd);
+/**
+ * lws_vfs_file_seek_set() - seek relative to start of file
+ *
+ * \param fop_fd: fop_fd we are seeking in
+ * \param offset: offset from start of file
+ */
+LWS_VISIBLE LWS_EXTERN lws_fileofs_t
+lws_vfs_file_seek_set(lws_fop_fd_t fop_fd, lws_fileofs_t offset);
+/**
+ * lws_vfs_file_seek_end() - seek relative to end of file
+ *
+ * \param fop_fd: fop_fd we are seeking in
+ * \param offset: offset from start of file
+ */
+LWS_VISIBLE LWS_EXTERN lws_fileofs_t
+lws_vfs_file_seek_end(lws_fop_fd_t fop_fd, lws_fileofs_t offset);
+
+extern struct lws_plat_file_ops fops_zip;
+
+/**
+ * lws_plat_file_open() - open vfs filepath
+ *
+ * \param fops: file ops struct that applies to this descriptor
+ * \param vfs_path: filename to open
+ * \param flags: pointer to open flags
+ *
+ * The vfs_path is scanned for known fops signatures, and the open directed
+ * to any matching fops open.
+ *
+ * User code should use this api to perform vfs opens.
+ *
+ * returns semi-opaque handle
+ */
+LWS_VISIBLE LWS_EXTERN lws_fop_fd_t LWS_WARN_UNUSED_RESULT
+lws_vfs_file_open(const struct lws_plat_file_ops *fops, const char *vfs_path,
+ lws_fop_flags_t *flags);
+
+/**
+ * lws_plat_file_close() - close file
+ *
+ * \param fop_fd: file handle to close
+ */
+static LWS_INLINE int
+lws_vfs_file_close(lws_fop_fd_t *fop_fd)
+{
+ if (*fop_fd && (*fop_fd)->fops)
+ return (*fop_fd)->fops->LWS_FOP_CLOSE(fop_fd);
+
+ return 0;
+}
+
+/**
+ * lws_plat_file_seek_cur() - close file
+ *
+ *
+ * \param fop_fd: file handle
+ * \param offset: position to seek to
+ */
+static LWS_INLINE lws_fileofs_t
+lws_vfs_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset)
+{
+ return fop_fd->fops->LWS_FOP_SEEK_CUR(fop_fd, offset);
+}
+/**
+ * lws_plat_file_read() - read from file
+ *
+ * \param fop_fd: file handle
+ * \param amount: how much to read (rewritten by call)
+ * \param buf: buffer to write to
+ * \param len: max length
+ */
+static LWS_INLINE int LWS_WARN_UNUSED_RESULT
+lws_vfs_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
+ uint8_t *buf, lws_filepos_t len)
+{
+ return fop_fd->fops->LWS_FOP_READ(fop_fd, amount, buf, len);
+}
+/**
+ * lws_plat_file_write() - write from file
+ *
+ * \param fop_fd: file handle
+ * \param amount: how much to write (rewritten by call)
+ * \param buf: buffer to read from
+ * \param len: max length
+ */
+static LWS_INLINE int LWS_WARN_UNUSED_RESULT
+lws_vfs_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
+ uint8_t *buf, lws_filepos_t len)
+{
+ return fop_fd->fops->LWS_FOP_WRITE(fop_fd, amount, buf, len);
+}
+
+/* these are the platform file operations implementations... they can
+ * be called directly and used in fops arrays
+ */
+
+LWS_VISIBLE LWS_EXTERN lws_fop_fd_t
+_lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename,
+ const char *vpath, lws_fop_flags_t *flags);
+LWS_VISIBLE LWS_EXTERN int
+_lws_plat_file_close(lws_fop_fd_t *fop_fd);
+LWS_VISIBLE LWS_EXTERN lws_fileofs_t
+_lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset);
+LWS_VISIBLE LWS_EXTERN int
+_lws_plat_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
+ uint8_t *buf, lws_filepos_t len);
+LWS_VISIBLE LWS_EXTERN int
+_lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
+ uint8_t *buf, lws_filepos_t len);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_alloc_vfs_file(struct lws_context *context, const char *filename,
+ uint8_t **buf, lws_filepos_t *amount);
+//@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*! \defgroup sending-data Sending data
+
+ APIs related to writing data on a connection
+*/
+//@{
+#if !defined(LWS_SIZEOFPTR)
+#define LWS_SIZEOFPTR ((int)sizeof (void *))
+#endif
+
+#if defined(__x86_64__)
+#define _LWS_PAD_SIZE 16 /* Intel recommended for best performance */
+#else
+#define _LWS_PAD_SIZE LWS_SIZEOFPTR /* Size of a pointer on the target arch */
+#endif
+#define _LWS_PAD(n) (((n) % _LWS_PAD_SIZE) ? \
+ ((n) + (_LWS_PAD_SIZE - ((n) % _LWS_PAD_SIZE))) : (n))
+/* last 2 is for lws-meta */
+#define LWS_PRE _LWS_PAD(4 + 10 + 2)
+/* used prior to 1.7 and retained for backward compatibility */
+#define LWS_SEND_BUFFER_PRE_PADDING LWS_PRE
+#define LWS_SEND_BUFFER_POST_PADDING 0
+
+#define LWS_WRITE_RAW LWS_WRITE_HTTP
+
+/*
+ * NOTE: These public enums are part of the abi. If you want to add one,
+ * add it at where specified so existing users are unaffected.
+ */
+enum lws_write_protocol {
+ LWS_WRITE_TEXT = 0,
+ /**< Send a ws TEXT message,the pointer must have LWS_PRE valid
+ * memory behind it.
+ *
+ * The receiver expects only valid utf-8 in the payload */
+ LWS_WRITE_BINARY = 1,
+ /**< Send a ws BINARY message, the pointer must have LWS_PRE valid
+ * memory behind it.
+ *
+ * Any sequence of bytes is valid */
+ LWS_WRITE_CONTINUATION = 2,
+ /**< Continue a previous ws message, the pointer must have LWS_PRE valid
+ * memory behind it */
+ LWS_WRITE_HTTP = 3,
+ /**< Send HTTP content */
+
+ /* LWS_WRITE_CLOSE is handled by lws_close_reason() */
+ LWS_WRITE_PING = 5,
+ LWS_WRITE_PONG = 6,
+
+ /* Same as write_http but we know this write ends the transaction */
+ LWS_WRITE_HTTP_FINAL = 7,
+
+ /* HTTP2 */
+
+ LWS_WRITE_HTTP_HEADERS = 8,
+ /**< Send http headers (http2 encodes this payload and LWS_WRITE_HTTP
+ * payload differently, http 1.x links also handle this correctly. so
+ * to be compatible with both in the future,header response part should
+ * be sent using this regardless of http version expected)
+ */
+ LWS_WRITE_HTTP_HEADERS_CONTINUATION = 9,
+ /**< Continuation of http/2 headers
+ */
+
+ /****** add new things just above ---^ ******/
+
+ /* flags */
+
+ LWS_WRITE_BUFLIST = 0x20,
+ /**< Don't actually write it... stick it on the output buflist and
+ * write it as soon as possible. Useful if you learn you have to
+ * write something, have the data to write to hand but the timing is
+ * unrelated as to whether the connection is writable or not, and were
+ * otherwise going to have to allocate a temp buffer and write it
+ * later anyway */
+
+ LWS_WRITE_NO_FIN = 0x40,
+ /**< This part of the message is not the end of the message */
+
+ LWS_WRITE_H2_STREAM_END = 0x80,
+ /**< Flag indicates this packet should go out with STREAM_END if h2
+ * STREAM_END is allowed on DATA or HEADERS.
+ */
+
+ LWS_WRITE_CLIENT_IGNORE_XOR_MASK = 0x80
+ /**< client packet payload goes out on wire unmunged
+ * only useful for security tests since normal servers cannot
+ * decode the content if used */
+};
+
+/* used with LWS_CALLBACK_CHILD_WRITE_VIA_PARENT */
+
+struct lws_write_passthru {
+ struct lws *wsi;
+ unsigned char *buf;
+ size_t len;
+ enum lws_write_protocol wp;
+};
+
+
+/**
+ * lws_write() - Apply protocol then write data to client
+ *
+ * \param wsi: Websocket instance (available from user callback)
+ * \param buf: The data to send. For data being sent on a websocket
+ * connection (ie, not default http), this buffer MUST have
+ * LWS_PRE bytes valid BEFORE the pointer.
+ * This is so the protocol header data can be added in-situ.
+ * \param len: Count of the data bytes in the payload starting from buf
+ * \param protocol: Use LWS_WRITE_HTTP to reply to an http connection, and one
+ * of LWS_WRITE_BINARY or LWS_WRITE_TEXT to send appropriate
+ * data on a websockets connection. Remember to allow the extra
+ * bytes before and after buf if LWS_WRITE_BINARY or LWS_WRITE_TEXT
+ * are used.
+ *
+ * This function provides the way to issue data back to the client
+ * for both http and websocket protocols.
+ *
+ * IMPORTANT NOTICE!
+ *
+ * When sending with websocket protocol
+ *
+ * LWS_WRITE_TEXT,
+ * LWS_WRITE_BINARY,
+ * LWS_WRITE_CONTINUATION,
+ * LWS_WRITE_PING,
+ * LWS_WRITE_PONG,
+ *
+ * or sending on http/2,
+ *
+ * the send buffer has to have LWS_PRE bytes valid BEFORE the buffer pointer you
+ * pass to lws_write(). Since you'll probably want to use http/2 before too
+ * long, it's wise to just always do this with lws_write buffers... LWS_PRE is
+ * typically 16 bytes it's not going to hurt usually.
+ *
+ * start of alloc ptr passed to lws_write end of allocation
+ * | | |
+ * v <-- LWS_PRE bytes --> v v
+ * [---------------- allocated memory ---------------]
+ * (for lws use) [====== user buffer ======]
+ *
+ * This allows us to add protocol info before and after the data, and send as
+ * one packet on the network without payload copying, for maximum efficiency.
+ *
+ * So for example you need this kind of code to use lws_write with a
+ * 128-byte payload
+ *
+ * char buf[LWS_PRE + 128];
+ *
+ * // fill your part of the buffer... for example here it's all zeros
+ * memset(&buf[LWS_PRE], 0, 128);
+ *
+ * lws_write(wsi, &buf[LWS_PRE], 128, LWS_WRITE_TEXT);
+ *
+ * LWS_PRE is at least the frame nonce + 2 header + 8 length
+ * LWS_SEND_BUFFER_POST_PADDING is deprecated, it's now 0 and can be left off.
+ * The example apps no longer use it.
+ *
+ * Pad LWS_PRE to the CPU word size, so that word references
+ * to the address immediately after the padding won't cause an unaligned access
+ * error. Sometimes for performance reasons the recommended padding is even
+ * larger than sizeof(void *).
+ *
+ * In the case of sending using websocket protocol, be sure to allocate
+ * valid storage before and after buf as explained above. This scheme
+ * allows maximum efficiency of sending data and protocol in a single
+ * packet while not burdening the user code with any protocol knowledge.
+ *
+ * Return may be -1 for a fatal error needing connection close, or the
+ * number of bytes sent.
+ *
+ * Truncated Writes
+ * ================
+ *
+ * The OS may not accept everything you asked to write on the connection.
+ *
+ * Posix defines POLLOUT indication from poll() to show that the connection
+ * will accept more write data, but it doesn't specifiy how much. It may just
+ * accept one byte of whatever you wanted to send.
+ *
+ * LWS will buffer the remainder automatically, and send it out autonomously.
+ *
+ * During that time, WRITABLE callbacks will be suppressed.
+ *
+ * This is to handle corner cases where unexpectedly the OS refuses what we
+ * usually expect it to accept. You should try to send in chunks that are
+ * almost always accepted in order to avoid the inefficiency of the buffering.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_write(struct lws *wsi, unsigned char *buf, size_t len,
+ enum lws_write_protocol protocol);
+
+/* helper for case where buffer may be const */
+#define lws_write_http(wsi, buf, len) \
+ lws_write(wsi, (unsigned char *)(buf), len, LWS_WRITE_HTTP)
+
+/**
+ * lws_write_ws_flags() - Helper for multi-frame ws message flags
+ *
+ * \param initial: the lws_write flag to use for the start fragment, eg,
+ * LWS_WRITE_TEXT
+ * \param is_start: nonzero if this is the first fragment of the message
+ * \param is_end: nonzero if this is the last fragment of the message
+ *
+ * Returns the correct LWS_WRITE_ flag to use for each fragment of a message
+ * in turn.
+ */
+static LWS_INLINE int
+lws_write_ws_flags(int initial, int is_start, int is_end)
+{
+ int r;
+
+ if (is_start)
+ r = initial;
+ else
+ r = LWS_WRITE_CONTINUATION;
+
+ if (!is_end)
+ r |= LWS_WRITE_NO_FIN;
+
+ return r;
+}
+
+/**
+ * lws_raw_transaction_completed() - Helper for flushing before close
+ *
+ * \param wsi: the struct lws to operate on
+ *
+ * Returns -1 if the wsi can close now. However if there is buffered, unsent
+ * data, the wsi is marked as to be closed when the output buffer data is
+ * drained, and it returns 0.
+ *
+ * For raw cases where the transaction completed without failure,
+ * `return lws_raw_transaction_completed(wsi)` should better be used than
+ * return -1.
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_raw_transaction_completed(struct lws *wsi);
+
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/** \defgroup callback-when-writeable Callback when writeable
+ *
+ * ##Callback When Writeable
+ *
+ * lws can only write data on a connection when it is able to accept more
+ * data without blocking.
+ *
+ * So a basic requirement is we should only use the lws_write() apis when the
+ * connection we want to write on says that he can accept more data.
+ *
+ * When lws cannot complete your send at the time, it will buffer the data
+ * and send it in the background, suppressing any further WRITEABLE callbacks
+ * on that connection until it completes. So it is important to write new
+ * things in a new writeable callback.
+ *
+ * These apis reflect the various ways we can indicate we would like to be
+ * called back when one or more connections is writeable.
+ */
+///@{
+
+/**
+ * lws_callback_on_writable() - Request a callback when this socket
+ * becomes able to be written to without
+ * blocking
+ *
+ * \param wsi: Websocket connection instance to get callback for
+ *
+ * - Which: only this wsi
+ * - When: when the individual connection becomes writeable
+ * - What: LWS_CALLBACK_*_WRITEABLE
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_callback_on_writable(struct lws *wsi);
+
+/**
+ * lws_callback_on_writable_all_protocol() - Request a callback for all
+ * connections using the given protocol when it
+ * becomes possible to write to each socket without
+ * blocking in turn.
+ *
+ * \param context: lws_context
+ * \param protocol: Protocol whose connections will get callbacks
+ *
+ * - Which: connections using this protocol on ANY VHOST
+ * - When: when the individual connection becomes writeable
+ * - What: LWS_CALLBACK_*_WRITEABLE
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_callback_on_writable_all_protocol(const struct lws_context *context,
+ const struct lws_protocols *protocol);
+
+/**
+ * lws_callback_on_writable_all_protocol_vhost() - Request a callback for
+ * all connections on same vhost using the given protocol
+ * when it becomes possible to write to each socket without
+ * blocking in turn.
+ *
+ * \param vhost: Only consider connections on this lws_vhost
+ * \param protocol: Protocol whose connections will get callbacks
+ *
+ * - Which: connections using this protocol on GIVEN VHOST ONLY
+ * - When: when the individual connection becomes writeable
+ * - What: LWS_CALLBACK_*_WRITEABLE
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost,
+ const struct lws_protocols *protocol);
+
+/**
+ * lws_callback_all_protocol() - Callback all connections using
+ * the given protocol with the given reason
+ *
+ * \param context: lws_context
+ * \param protocol: Protocol whose connections will get callbacks
+ * \param reason: Callback reason index
+ *
+ * - Which: connections using this protocol on ALL VHOSTS
+ * - When: before returning
+ * - What: reason
+ *
+ * This isn't normally what you want... normally any update of connection-
+ * specific information can wait until a network-related callback like rx,
+ * writable, or close.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_callback_all_protocol(struct lws_context *context,
+ const struct lws_protocols *protocol, int reason);
+
+/**
+ * lws_callback_all_protocol_vhost() - Callback all connections using
+ * the given protocol with the given reason. This is
+ * deprecated since v2.4: use lws_callback_all_protocol_vhost_args
+ *
+ * \param vh: Vhost whose connections will get callbacks
+ * \param protocol: Which protocol to match. NULL means all.
+ * \param reason: Callback reason index
+ *
+ * - Which: connections using this protocol on GIVEN VHOST ONLY
+ * - When: now
+ * - What: reason
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_callback_all_protocol_vhost(struct lws_vhost *vh,
+ const struct lws_protocols *protocol,
+ int reason)
+LWS_WARN_DEPRECATED;
+
+/**
+ * lws_callback_all_protocol_vhost_args() - Callback all connections using
+ * the given protocol with the given reason and args
+ *
+ * \param vh: Vhost whose connections will get callbacks
+ * \param protocol: Which protocol to match. NULL means all.
+ * \param reason: Callback reason index
+ * \param argp: Callback "in" parameter
+ * \param len: Callback "len" parameter
+ *
+ * - Which: connections using this protocol on GIVEN VHOST ONLY
+ * - When: now
+ * - What: reason
+ */
+LWS_VISIBLE int
+lws_callback_all_protocol_vhost_args(struct lws_vhost *vh,
+ const struct lws_protocols *protocol,
+ int reason, void *argp, size_t len);
+
+/**
+ * lws_callback_vhost_protocols() - Callback all protocols enabled on a vhost
+ * with the given reason
+ *
+ * \param wsi: wsi whose vhost will get callbacks
+ * \param reason: Callback reason index
+ * \param in: in argument to callback
+ * \param len: len argument to callback
+ *
+ * - Which: connections using this protocol on same VHOST as wsi ONLY
+ * - When: now
+ * - What: reason
+ *
+ * This is deprecated since v2.5, use lws_callback_vhost_protocols_vhost()
+ * which takes the pointer to the vhost directly without using or needing the
+ * wsi.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_callback_vhost_protocols(struct lws *wsi, int reason, void *in, int len)
+LWS_WARN_DEPRECATED;
+
+/**
+ * lws_callback_vhost_protocols_vhost() - Callback all protocols enabled on a vhost
+ * with the given reason
+ *
+ * \param vh: vhost that will get callbacks
+ * \param reason: Callback reason index
+ * \param in: in argument to callback
+ * \param len: len argument to callback
+ *
+ * - Which: connections using this protocol on same VHOST as wsi ONLY
+ * - When: now
+ * - What: reason
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_callback_vhost_protocols_vhost(struct lws_vhost *vh, int reason, void *in,
+ size_t len);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
+ void *user, void *in, size_t len);
+
+/**
+ * lws_get_socket_fd() - returns the socket file descriptor
+ *
+ * This is needed to use sendto() on UDP raw sockets
+ *
+ * \param wsi: Websocket connection instance
+ */
+LWS_VISIBLE LWS_EXTERN lws_sockfd_type
+lws_get_socket_fd(struct lws *wsi);
+
+/**
+ * lws_get_peer_write_allowance() - get the amount of data writeable to peer
+ * if known
+ *
+ * \param wsi: Websocket connection instance
+ *
+ * if the protocol does not have any guidance, returns -1. Currently only
+ * http2 connections get send window information from this API. But your code
+ * should use it so it can work properly with any protocol.
+ *
+ * If nonzero return is the amount of payload data the peer or intermediary has
+ * reported it has buffer space for. That has NO relationship with the amount
+ * of buffer space your OS can accept on this connection for a write action.
+ *
+ * This number represents the maximum you could send to the peer or intermediary
+ * on this connection right now without the protocol complaining.
+ *
+ * lws manages accounting for send window updates and payload writes
+ * automatically, so this number reflects the situation at the peer or
+ * intermediary dynamically.
+ */
+LWS_VISIBLE LWS_EXTERN lws_fileofs_t
+lws_get_peer_write_allowance(struct lws *wsi);
+
+/**
+ * lws_wsi_tx_credit() - get / set generic tx credit if role supports it
+ *
+ * \param wsi: connection to set / get tx credit on
+ * \param peer_to_us: 0 = set / get us-to-peer direction, else peer-to-us
+ * \param add: amount of credit to add
+ *
+ * If the wsi does not support tx credit, returns 0.
+ *
+ * If add is zero, returns one of the wsi tx credit values for the wsi.
+ * If add is nonzero, \p add is added to the selected tx credit value
+ * for the wsi.
+ */
+#define LWSTXCR_US_TO_PEER 0
+#define LWSTXCR_PEER_TO_US 1
+
+LWS_VISIBLE LWS_EXTERN int
+lws_wsi_tx_credit(struct lws *wsi, char peer_to_us, int add);
+
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*! \defgroup wsclose Websocket Close
+ *
+ * ##Websocket close frame control
+ *
+ * When we close a ws connection, we can send a reason code and a short
+ * UTF-8 description back with the close packet.
+ */
+///@{
+
+/*
+ * NOTE: These public enums are part of the abi. If you want to add one,
+ * add it at where specified so existing users are unaffected.
+ */
+/** enum lws_close_status - RFC6455 close status codes */
+enum lws_close_status {
+ LWS_CLOSE_STATUS_NOSTATUS = 0,
+ LWS_CLOSE_STATUS_NORMAL = 1000,
+ /**< 1000 indicates a normal closure, meaning that the purpose for
+ which the connection was established has been fulfilled. */
+ LWS_CLOSE_STATUS_GOINGAWAY = 1001,
+ /**< 1001 indicates that an endpoint is "going away", such as a server
+ going down or a browser having navigated away from a page. */
+ LWS_CLOSE_STATUS_PROTOCOL_ERR = 1002,
+ /**< 1002 indicates that an endpoint is terminating the connection due
+ to a protocol error. */
+ LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE = 1003,
+ /**< 1003 indicates that an endpoint is terminating the connection
+ because it has received a type of data it cannot accept (e.g., an
+ endpoint that understands only text data MAY send this if it
+ receives a binary message). */
+ LWS_CLOSE_STATUS_RESERVED = 1004,
+ /**< Reserved. The specific meaning might be defined in the future. */
+ LWS_CLOSE_STATUS_NO_STATUS = 1005,
+ /**< 1005 is a reserved value and MUST NOT be set as a status code in a
+ Close control frame by an endpoint. It is designated for use in
+ applications expecting a status code to indicate that no status
+ code was actually present. */
+ LWS_CLOSE_STATUS_ABNORMAL_CLOSE = 1006,
+ /**< 1006 is a reserved value and MUST NOT be set as a status code in a
+ Close control frame by an endpoint. It is designated for use in
+ applications expecting a status code to indicate that the
+ connection was closed abnormally, e.g., without sending or
+ receiving a Close control frame. */
+ LWS_CLOSE_STATUS_INVALID_PAYLOAD = 1007,
+ /**< 1007 indicates that an endpoint is terminating the connection
+ because it has received data within a message that was not
+ consistent with the type of the message (e.g., non-UTF-8 [RFC3629]
+ data within a text message). */
+ LWS_CLOSE_STATUS_POLICY_VIOLATION = 1008,
+ /**< 1008 indicates that an endpoint is terminating the connection
+ because it has received a message that violates its policy. This
+ is a generic status code that can be returned when there is no
+ other more suitable status code (e.g., 1003 or 1009) or if there
+ is a need to hide specific details about the policy. */
+ LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE = 1009,
+ /**< 1009 indicates that an endpoint is terminating the connection
+ because it has received a message that is too big for it to
+ process. */
+ LWS_CLOSE_STATUS_EXTENSION_REQUIRED = 1010,
+ /**< 1010 indicates that an endpoint (client) is terminating the
+ connection because it has expected the server to negotiate one or
+ more extension, but the server didn't return them in the response
+ message of the WebSocket handshake. The list of extensions that
+ are needed SHOULD appear in the /reason/ part of the Close frame.
+ Note that this status code is not used by the server, because it
+ can fail the WebSocket handshake instead */
+ LWS_CLOSE_STATUS_UNEXPECTED_CONDITION = 1011,
+ /**< 1011 indicates that a server is terminating the connection because
+ it encountered an unexpected condition that prevented it from
+ fulfilling the request. */
+ LWS_CLOSE_STATUS_TLS_FAILURE = 1015,
+ /**< 1015 is a reserved value and MUST NOT be set as a status code in a
+ Close control frame by an endpoint. It is designated for use in
+ applications expecting a status code to indicate that the
+ connection was closed due to a failure to perform a TLS handshake
+ (e.g., the server certificate can't be verified). */
+
+ LWS_CLOSE_STATUS_CLIENT_TRANSACTION_DONE = 2000,
+
+ /****** add new things just above ---^ ******/
+
+ LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY = 9999,
+};
+
+/**
+ * lws_close_reason - Set reason and aux data to send with Close packet
+ * If you are going to return nonzero from the callback
+ * requesting the connection to close, you can optionally
+ * call this to set the reason the peer will be told if
+ * possible.
+ *
+ * \param wsi: The websocket connection to set the close reason on
+ * \param status: A valid close status from websocket standard
+ * \param buf: NULL or buffer containing up to 124 bytes of auxiliary data
+ * \param len: Length of data in \p buf to send
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_close_reason(struct lws *wsi, enum lws_close_status status,
+ unsigned char *buf, size_t len);
+
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*! \defgroup extensions Extension related functions
+ * ##Extension releated functions
+ *
+ * Ws defines optional extensions, lws provides the ability to implement these
+ * in user code if so desired.
+ *
+ * We provide one extensions permessage-deflate.
+ */
+///@{
+
+/*
+ * NOTE: These public enums are part of the abi. If you want to add one,
+ * add it at where specified so existing users are unaffected.
+ */
+enum lws_extension_callback_reasons {
+ LWS_EXT_CB_CONSTRUCT = 4,
+ LWS_EXT_CB_CLIENT_CONSTRUCT = 5,
+ LWS_EXT_CB_DESTROY = 8,
+ LWS_EXT_CB_PACKET_TX_PRESEND = 12,
+ LWS_EXT_CB_PAYLOAD_TX = 21,
+ LWS_EXT_CB_PAYLOAD_RX = 22,
+ LWS_EXT_CB_OPTION_DEFAULT = 23,
+ LWS_EXT_CB_OPTION_SET = 24,
+ LWS_EXT_CB_OPTION_CONFIRM = 25,
+ LWS_EXT_CB_NAMED_OPTION_SET = 26,
+
+ /****** add new things just above ---^ ******/
+};
+
+/** enum lws_ext_options_types */
+enum lws_ext_options_types {
+ EXTARG_NONE, /**< does not take an argument */
+ EXTARG_DEC, /**< requires a decimal argument */
+ EXTARG_OPT_DEC /**< may have an optional decimal argument */
+
+ /* Add new things just above here ---^
+ * This is part of the ABI, don't needlessly break compatibility */
+};
+
+/** struct lws_ext_options - Option arguments to the extension. These are
+ * used in the negotiation at ws upgrade time.
+ * The helper function lws_ext_parse_options()
+ * uses these to generate callbacks */
+struct lws_ext_options {
+ const char *name; /**< Option name, eg, "server_no_context_takeover" */
+ enum lws_ext_options_types type; /**< What kind of args the option can take */
+
+ /* Add new things just above here ---^
+ * This is part of the ABI, don't needlessly break compatibility */
+};
+
+/** struct lws_ext_option_arg */
+struct lws_ext_option_arg {
+ const char *option_name; /**< may be NULL, option_index used then */
+ int option_index; /**< argument ordinal to use if option_name missing */
+ const char *start; /**< value */
+ int len; /**< length of value */
+};
+
+/**
+ * typedef lws_extension_callback_function() - Hooks to allow extensions to operate
+ * \param context: Websockets context
+ * \param ext: This extension
+ * \param wsi: Opaque websocket instance pointer
+ * \param reason: The reason for the call
+ * \param user: Pointer to ptr to per-session user data allocated by library
+ * \param in: Pointer used for some callback reasons
+ * \param len: Length set for some callback reasons
+ *
+ * Each extension that is active on a particular connection receives
+ * callbacks during the connection lifetime to allow the extension to
+ * operate on websocket data and manage itself.
+ *
+ * Libwebsockets takes care of allocating and freeing "user" memory for
+ * each active extension on each connection. That is what is pointed to
+ * by the user parameter.
+ *
+ * LWS_EXT_CB_CONSTRUCT: called when the server has decided to
+ * select this extension from the list provided by the client,
+ * just before the server will send back the handshake accepting
+ * the connection with this extension active. This gives the
+ * extension a chance to initialize its connection context found
+ * in user.
+ *
+ * LWS_EXT_CB_CLIENT_CONSTRUCT: same as LWS_EXT_CB_CONSTRUCT
+ * but called when client is instantiating this extension. Some
+ * extensions will work the same on client and server side and then
+ * you can just merge handlers for both CONSTRUCTS.
+ *
+ * LWS_EXT_CB_DESTROY: called when the connection the extension was
+ * being used on is about to be closed and deallocated. It's the
+ * last chance for the extension to deallocate anything it has
+ * allocated in the user data (pointed to by user) before the
+ * user data is deleted. This same callback is used whether you
+ * are in client or server instantiation context.
+ *
+ * LWS_EXT_CB_PACKET_TX_PRESEND: this works the same way as
+ * LWS_EXT_CB_PACKET_RX_PREPARSE above, except it gives the
+ * extension a chance to change websocket data just before it will
+ * be sent out. Using the same lws_token pointer scheme in in,
+ * the extension can change the buffer and the length to be
+ * transmitted how it likes. Again if it wants to grow the
+ * buffer safely, it should copy the data into its own buffer and
+ * set the lws_tokens token pointer to it.
+ *
+ * LWS_EXT_CB_ARGS_VALIDATE:
+ */
+typedef int
+lws_extension_callback_function(struct lws_context *context,
+ const struct lws_extension *ext, struct lws *wsi,
+ enum lws_extension_callback_reasons reason,
+ void *user, void *in, size_t len);
+
+/** struct lws_extension - An extension we support */
+struct lws_extension {
+ const char *name; /**< Formal extension name, eg, "permessage-deflate" */
+ lws_extension_callback_function *callback; /**< Service callback */
+ const char *client_offer; /**< String containing exts and options client offers */
+
+ /* Add new things just above here ---^
+ * This is part of the ABI, don't needlessly break compatibility */
+};
+
+/**
+ * lws_set_extension_option(): set extension option if possible
+ *
+ * \param wsi: websocket connection
+ * \param ext_name: name of ext, like "permessage-deflate"
+ * \param opt_name: name of option, like "rx_buf_size"
+ * \param opt_val: value to set option to
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_set_extension_option(struct lws *wsi, const char *ext_name,
+ const char *opt_name, const char *opt_val);
+
+/**
+ * lws_ext_parse_options() - deal with parsing negotiated extension options
+ *
+ * \param ext: related extension struct
+ * \param wsi: websocket connection
+ * \param ext_user: per-connection extension private data
+ * \param opts: list of supported options
+ * \param o: option string to parse
+ * \param len: length
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi,
+ void *ext_user, const struct lws_ext_options *opts,
+ const char *o, int len);
+
+/** lws_extension_callback_pm_deflate() - extension for RFC7692
+ *
+ * \param context: lws context
+ * \param ext: related lws_extension struct
+ * \param wsi: websocket connection
+ * \param reason: incoming callback reason
+ * \param user: per-connection extension private data
+ * \param in: pointer parameter
+ * \param len: length parameter
+ *
+ * Built-in callback implementing RFC7692 permessage-deflate
+ */
+LWS_EXTERN int
+lws_extension_callback_pm_deflate(struct lws_context *context,
+ const struct lws_extension *ext,
+ struct lws *wsi,
+ enum lws_extension_callback_reasons reason,
+ void *user, void *in, size_t len);
+
+/*
+ * The internal exts are part of the public abi
+ * If we add more extensions, publish the callback here ------v
+ */
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/** \defgroup wsstatus Websocket status APIs
+ * ##Websocket connection status APIs
+ *
+ * These provide information about ws connection or message status
+ */
+///@{
+/**
+ * lws_send_pipe_choked() - tests if socket is writable or not
+ * \param wsi: lws connection
+ *
+ * Allows you to check if you can write more on the socket
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_send_pipe_choked(struct lws *wsi);
+
+/**
+ * lws_is_final_fragment() - tests if last part of ws message
+ *
+ * \param wsi: lws connection
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_is_final_fragment(struct lws *wsi);
+
+/**
+ * lws_is_first_fragment() - tests if first part of ws message
+ *
+ * \param wsi: lws connection
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_is_first_fragment(struct lws *wsi);
+
+/**
+ * lws_get_reserved_bits() - access reserved bits of ws frame
+ * \param wsi: lws connection
+ */
+LWS_VISIBLE LWS_EXTERN unsigned char
+lws_get_reserved_bits(struct lws *wsi);
+
+/**
+ * lws_partial_buffered() - find out if lws buffered the last write
+ * \param wsi: websocket connection to check
+ *
+ * Returns 1 if you cannot use lws_write because the last
+ * write on this connection is still buffered, and can't be cleared without
+ * returning to the service loop and waiting for the connection to be
+ * writeable again.
+ *
+ * If you will try to do >1 lws_write call inside a single
+ * WRITEABLE callback, you must check this after every write and bail if
+ * set, ask for a new writeable callback and continue writing from there.
+ *
+ * This is never set at the start of a writeable callback, but any write
+ * may set it.
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_partial_buffered(struct lws *wsi);
+
+/**
+ * lws_frame_is_binary(): true if the current frame was sent in binary mode
+ *
+ * \param wsi: the connection we are inquiring about
+ *
+ * This is intended to be called from the LWS_CALLBACK_RECEIVE callback if
+ * it's interested to see if the frame it's dealing with was sent in binary
+ * mode.
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_frame_is_binary(struct lws *wsi);
+///@}
--- /dev/null
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+enum lws_tls_cert_info {
+ LWS_TLS_CERT_INFO_VALIDITY_FROM,
+ /**< fills .time with the time_t the cert validity started from */
+ LWS_TLS_CERT_INFO_VALIDITY_TO,
+ /**< fills .time with the time_t the cert validity ends at */
+ LWS_TLS_CERT_INFO_COMMON_NAME,
+ /**< fills up to len bytes of .ns.name with the cert common name */
+ LWS_TLS_CERT_INFO_ISSUER_NAME,
+ /**< fills up to len bytes of .ns.name with the cert issuer name */
+ LWS_TLS_CERT_INFO_USAGE,
+ /**< fills verified with a bitfield asserting the valid uses */
+ LWS_TLS_CERT_INFO_VERIFIED,
+ /**< fills .verified with a bool representing peer cert validity,
+ * call returns -1 if no cert */
+ LWS_TLS_CERT_INFO_OPAQUE_PUBLIC_KEY,
+ /**< the certificate's public key, as an opaque bytestream. These
+ * opaque bytestreams can only be compared with each other using the
+ * same tls backend, ie, OpenSSL or mbedTLS. The different backends
+ * produce different, incompatible representations for the same cert.
+ */
+};
+
+union lws_tls_cert_info_results {
+ unsigned int verified;
+ time_t time;
+ unsigned int usage;
+ struct {
+ int len;
+ /* KEEP LAST... notice the [64] is only there because
+ * name[] is not allowed in a union. The actual length of
+ * name[] is arbitrary and is passed into the api using the
+ * len parameter. Eg
+ *
+ * char big[1024];
+ * union lws_tls_cert_info_results *buf =
+ * (union lws_tls_cert_info_results *)big;
+ *
+ * lws_tls_peer_cert_info(wsi, type, buf, sizeof(big) -
+ * sizeof(*buf) + sizeof(buf->ns.name));
+ */
+ char name[64];
+ } ns;
+};
+
+struct lws_x509_cert;
+struct lws_jwk;
+
+/**
+ * lws_x509_create() - Allocate an lws_x509_cert object
+ *
+ * \param x509: pointer to lws_x509_cert pointer to be set to allocated object
+ *
+ * Allocates an lws_x509_cert object and set *x509 to point to it.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_x509_create(struct lws_x509_cert **x509);
+
+/**
+ * lws_x509_parse_from_pem() - Read one or more x509 certs in PEM format from memory
+ *
+ * \param x509: pointer to lws_x509_cert object
+ * \param pem: pointer to PEM format content
+ * \param len: length of PEM format content
+ *
+ * Parses PEM certificates in memory into a native x509 representation for the
+ * TLS library. If there are multiple PEM certs concatenated, they are all
+ * read into the same object and exist as a "chain".
+ *
+ * IMPORTANT for compatibility with mbedtls, the last used byte of \p pem
+ * must be '\0' and the \p len must include it.
+ *
+ * Returns 0 if all went OK.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_x509_parse_from_pem(struct lws_x509_cert *x509, const void *pem, size_t len);
+
+/**
+ * lws_x509_verify() - Validate signing relationship between one or more certs
+ * and a trusted CA cert
+ *
+ * \param x509: pointer to lws_x509_cert object, may contain multiple
+ * \param trusted: a single, trusted cert object that we are checking for
+ * \param common_name: NULL, or required CN (Common Name) of \p x509
+ *
+ * Returns 0 if the cert or certs in \p x509 represent a complete chain that is
+ * ultimately signed by the cert in \p trusted. Returns nonzero if that's not
+ * the case.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_x509_verify(struct lws_x509_cert *x509, struct lws_x509_cert *trusted,
+ const char *common_name);
+
+/**
+ * lws_x509_public_to_jwk() - Copy the public key out of a cert and into a JWK
+ *
+ * \param jwk: pointer to the jwk to initialize and set to the public key
+ * \param x509: pointer to lws_x509_cert object that has the public key
+ * \param curves: NULL to disallow EC, else a comma-separated list of valid
+ * curves using the JWA naming, eg, "P-256,P-384,P-521".
+ * \param rsabits: minimum number of RSA bits required in the cert if RSA
+ *
+ * Returns 0 if JWK was set to the certificate public key correctly and the
+ * curve / the RSA key size was acceptable. Automatically produces an RSA or
+ * EC JWK depending on what the cert had.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_x509_public_to_jwk(struct lws_jwk *jwk, struct lws_x509_cert *x509,
+ const char *curves, int rsabits);
+
+/**
+ * lws_x509_jwk_privkey_pem() - Copy a private key PEM into a jwk that has the
+ * public part already
+ *
+ * \param jwk: pointer to the jwk to initialize and set to the public key
+ * \param pem: pointer to PEM private key in memory
+ * \param len: length of PEM private key in memory
+ * \param passphrase: NULL or passphrase needed to decrypt private key
+ *
+ * IMPORTANT for compatibility with mbedtls, the last used byte of \p pem
+ * must be '\0' and the \p len must include it.
+ *
+ * Returns 0 if the private key was successfully added to the JWK, else
+ * nonzero if failed.
+ *
+ * The PEM image in memory is zeroed down on both successful and failed exits.
+ * The caller should take care to zero down passphrase if used.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_x509_jwk_privkey_pem(struct lws_jwk *jwk, void *pem, size_t len,
+ const char *passphrase);
+
+/**
+ * lws_x509_destroy() - Destroy a previously allocated lws_x509_cert object
+ *
+ * \param x509: pointer to lws_x509_cert pointer
+ *
+ * Deallocates an lws_x509_cert object and sets its pointer to NULL.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_x509_destroy(struct lws_x509_cert **x509);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_x509_info(struct lws_x509_cert *x509, enum lws_tls_cert_info type,
+ union lws_tls_cert_info_results *buf, size_t len);
+
+/**
+ * lws_tls_peer_cert_info() - get information from the peer's TLS cert
+ *
+ * \param wsi: the connection to query
+ * \param type: one of LWS_TLS_CERT_INFO_
+ * \param buf: pointer to union to take result
+ * \param len: when result is a string, the true length of buf->ns.name[]
+ *
+ * lws_tls_peer_cert_info() lets you get hold of information from the peer
+ * certificate.
+ *
+ * Return 0 if there is a result in \p buf, or -1 indicating there was no cert
+ * or another problem.
+ *
+ * This function works the same no matter if the TLS backend is OpenSSL or
+ * mbedTLS.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type,
+ union lws_tls_cert_info_results *buf, size_t len);
+
+/**
+ * lws_tls_vhost_cert_info() - get information from the vhost's own TLS cert
+ *
+ * \param vhost: the vhost to query
+ * \param type: one of LWS_TLS_CERT_INFO_
+ * \param buf: pointer to union to take result
+ * \param len: when result is a string, the true length of buf->ns.name[]
+ *
+ * lws_tls_vhost_cert_info() lets you get hold of information from the vhost
+ * certificate.
+ *
+ * Return 0 if there is a result in \p buf, or -1 indicating there was no cert
+ * or another problem.
+ *
+ * This function works the same no matter if the TLS backend is OpenSSL or
+ * mbedTLS.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_tls_vhost_cert_info(struct lws_vhost *vhost, enum lws_tls_cert_info type,
+ union lws_tls_cert_info_results *buf, size_t len);
+
+/**
+ * lws_tls_acme_sni_cert_create() - creates a temp selfsigned cert
+ * and attaches to a vhost
+ *
+ * \param vhost: the vhost to acquire the selfsigned cert
+ * \param san_a: SAN written into the certificate
+ * \param san_b: second SAN written into the certificate
+ *
+ *
+ * Returns 0 if created and attached to the vhost. Returns -1 if problems and
+ * frees all allocations before returning.
+ *
+ * On success, any allocations are destroyed at vhost destruction automatically.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a,
+ const char *san_b);
+
+/**
+ * lws_tls_acme_sni_csr_create() - creates a CSR and related private key PEM
+ *
+ * \param context: lws_context used for random
+ * \param elements: array of LWS_TLS_REQ_ELEMENT_COUNT const char *
+ * \param csr: buffer that will get the b64URL(ASN-1 CSR)
+ * \param csr_len: max length of the csr buffer
+ * \param privkey_pem: pointer to pointer allocated to hold the privkey_pem
+ * \param privkey_len: pointer to size_t set to the length of the privkey_pem
+ *
+ * Creates a CSR according to the information in \p elements, and a private
+ * RSA key used to sign the CSR.
+ *
+ * The outputs are the b64URL(ASN-1 CSR) into csr, and the PEM private key into
+ * privkey_pem.
+ *
+ * Notice that \p elements points to an array of const char *s pointing to the
+ * information listed in the enum above. If an entry is NULL or an empty
+ * string, the element is set to "none" in the CSR.
+ *
+ * Returns 0 on success or nonzero for failure.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_tls_acme_sni_csr_create(struct lws_context *context, const char *elements[],
+ uint8_t *csr, size_t csr_len, char **privkey_pem,
+ size_t *privkey_len);
+
+/**
+ * lws_tls_cert_updated() - update every vhost using the given cert path
+ *
+ * \param context: our lws_context
+ * \param certpath: the filepath to the certificate
+ * \param keypath: the filepath to the private key of the certificate
+ * \param mem_cert: copy of the cert in memory
+ * \param len_mem_cert: length of the copy of the cert in memory
+ * \param mem_privkey: copy of the private key in memory
+ * \param len_mem_privkey: length of the copy of the private key in memory
+ *
+ * Checks every vhost to see if it is the using certificate described by the
+ * the given filepaths. If so, it attempts to update the vhost ssl_ctx to use
+ * the new certificate.
+ *
+ * Returns 0 on success or nonzero for failure.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_tls_cert_updated(struct lws_context *context, const char *certpath,
+ const char *keypath,
+ const char *mem_cert, size_t len_mem_cert,
+ const char *mem_privkey, size_t len_mem_privkey);
+
--- /dev/null
+/* lws_config.h Generated from lws_config.h.in */
+
+#ifndef NDEBUG
+ #ifndef _DEBUG
+ #define _DEBUG
+ #endif
+#endif
+
+#define LWS_INSTALL_DATADIR "/usr/local/share"
+#define LWS_LIBRARY_VERSION_MAJOR 4
+#define LWS_LIBRARY_VERSION_MINOR 0
+#define LWS_LIBRARY_VERSION_PATCH 22
+/* LWS_LIBRARY_VERSION_NUMBER looks like 1005001 for e.g. version 1.5.1 */
+#define LWS_LIBRARY_VERSION_NUMBER (LWS_LIBRARY_VERSION_MAJOR * 1000000) + \
+ (LWS_LIBRARY_VERSION_MINOR * 1000) + \
+ LWS_LIBRARY_VERSION_PATCH
+#define LWS_MAX_SMP 1
+
+/* #undef LWS_LIBRARY_VERSION_NUMBER */
+
+/* #undef LWS_AVOID_SIGPIPE_IGN */
+#define LWS_BUILD_HASH "d85b58c"
+/* #undef LWS_BUILTIN_GETIFADDRS */
+#define LWS_CLIENT_HTTP_PROXYING
+/* #undef LWS_DETECTED_PLAT_IOS */
+/* #undef LWS_FALLBACK_GETHOSTBYNAME */
+#define LWS_HAS_INTPTR_T
+#define LWS_HAS_GETOPT_LONG
+/* #undef LWS_HAVE__ATOI64 */
+#define LWS_HAVE_ATOLL
+#define LWS_HAVE_BN_bn2binpad
+#define LWS_HAVE_CLOCK_GETTIME
+#define LWS_HAVE_EC_POINT_get_affine_coordinates
+#define LWS_HAVE_ECDSA_SIG_set0
+#define LWS_HAVE_EVP_MD_CTX_free
+#define LWS_HAVE_EVP_aes_128_wrap
+#define LWS_HAVE_EVP_aes_128_cfb8
+#define LWS_HAVE_EVP_aes_128_cfb128
+#define LWS_HAVE_EVP_aes_192_cfb8
+#define LWS_HAVE_EVP_aes_192_cfb128
+#define LWS_HAVE_EVP_aes_256_cfb8
+#define LWS_HAVE_EVP_aes_256_cfb128
+#define LWS_HAVE_EVP_aes_128_xts
+#define LWS_HAVE_EXECVPE
+/* #undef LWS_HAVE_LIBCAP */
+#define LWS_HAVE_HMAC_CTX_new
+#define LWS_HAVE_MALLOC_H
+#define LWS_HAVE_MALLOC_TRIM
+#define LWS_HAVE_MALLOC_USABLE_SIZE
+/* #undef LWS_HAVE_mbedtls_net_init */
+/* #undef LWS_HAVE_mbedtls_ssl_conf_alpn_protocols */
+/* #undef LWS_HAVE_mbedtls_ssl_get_alpn_protocol */
+/* #undef LWS_HAVE_mbedtls_ssl_conf_sni */
+/* #undef LWS_HAVE_mbedtls_ssl_set_hs_ca_chain */
+/* #undef LWS_HAVE_mbedtls_ssl_set_hs_own_cert */
+/* #undef LWS_HAVE_mbedtls_ssl_set_hs_authmode */
+/* #undef LWS_HAVE_MBEDTLS_NET_SOCKETS */
+/* #undef LWS_HAVE_NEW_UV_VERSION_H */
+#define LWS_HAVE_OPENSSL_ECDH_H
+#define LWS_HAVE_PIPE2
+#define LWS_HAVE_EVENTFD
+#define LWS_HAVE_PTHREAD_H
+#define LWS_HAVE_RSA_SET0_KEY
+/* #undef LWS_HAVE_RSA_verify_pss_mgf1 */
+#define LWS_HAVE_SSL_CTX_get0_certificate
+#define LWS_HAVE_SSL_CTX_set1_param
+#define LWS_HAVE_SSL_CTX_set_ciphersuites
+#define LWS_HAVE_SSL_EXTRA_CHAIN_CERTS
+#define LWS_HAVE_SSL_get0_alpn_selected
+#define LWS_HAVE_SSL_CTX_EVP_PKEY_new_raw_private_key
+#define LWS_HAVE_SSL_set_alpn_protos
+#define LWS_HAVE_SSL_SET_INFO_CALLBACK
+/* #undef LWS_HAVE__STAT32I64 */
+#define LWS_HAVE_STDINT_H
+/* #undef LWS_HAVE_SYS_CAPABILITY_H */
+#define LWS_HAVE_TLS_CLIENT_METHOD
+#define LWS_HAVE_TLSV1_2_CLIENT_METHOD
+/* #undef LWS_HAVE_UV_VERSION_H */
+#define LWS_HAVE_VFORK
+#define LWS_HAVE_X509_get_key_usage
+#define LWS_HAVE_X509_VERIFY_PARAM_set1_host
+#define LWS_LIBRARY_VERSION "4.0.22"
+#define LWS_LOGGING_BITFIELD_CLEAR 0
+#define LWS_LOGGING_BITFIELD_SET 0
+/* #undef LWS_MINGW_SUPPORT */
+/* #undef LWS_NO_CLIENT */
+#define LWS_NO_DAEMONIZE
+#define LWS_OPENSSL_CLIENT_CERTS "../share"
+#define LWS_OPENSSL_SUPPORT
+/* #undef LWS_PLAT_OPTEE */
+#define LWS_PLAT_UNIX
+/* #undef LWS_PLAT_FREERTOS */
+/* #undef LWS_ROLE_CGI */
+/* #undef LWS_ROLE_DBUS */
+#define LWS_ROLE_H1
+#define LWS_ROLE_H2
+#define LWS_ROLE_RAW
+#define LWS_ROLE_RAW_FILE
+/* #undef LWS_ROLE_RAW_PROXY */
+#define LWS_ROLE_WS
+/* #undef LWS_ROLE_MQTT */
+/* #undef LWS_SHA1_USE_OPENSSL_NAME */
+#define LWS_SSL_CLIENT_USE_OS_CA_CERTS
+/* #undef LWS_SSL_SERVER_WITH_ECDH_CERT */
+/* #undef LWS_WITH_ABSTRACT */
+/* #undef LWS_WITH_ACCESS_LOG */
+/* #undef LWS_WITH_ACME */
+/* #undef LWS_WITH_ALSA */
+/* #undef LWS_WITH_SYS_ASYNC_DNS */
+/* #undef LWS_WITH_BORINGSSL */
+/* #undef LWS_WITH_CGI */
+#define LWS_WITH_CUSTOM_HEADERS
+/* #undef LWS_WITH_DEPRECATED_LWS_DLL */
+/* #undef LWS_WITH_DETAILED_LATENCY */
+#define LWS_WITH_DIR
+/* #undef LWS_WITH_ESP32 */
+/* #undef LWS_HAVE_EVBACKEND_LINUXAIO */
+/* #undef LWS_HAVE_EVBACKEND_IOURING */
+#define LWS_WITH_EXTERNAL_POLL
+#define LWS_WITH_FILE_OPS
+/* #undef LWS_WITH_FSMOUNT */
+/* #undef LWS_WITH_FTS */
+/* #undef LWS_WITH_GENCRYPTO */
+/* #undef LWS_WITH_GENERIC_SESSIONS */
+/* #undef LWS_WITH_GLIB */
+/* #undef LWS_WITH_GTK */
+#define LWS_WITH_HTTP2
+#define LWS_WITH_HTTP_BASIC_AUTH
+/* #undef LWS_WITH_HTTP_BROTLI */
+/* #undef LWS_WITH_HTTP_PROXY */
+/* #undef LWS_WITH_HTTP_STREAM_COMPRESSION */
+#define LWS_WITH_HTTP_UNCOMMON_HEADERS
+#define LWS_WITH_IPV6
+/* #undef LWS_WITH_JOSE */
+#define LWS_WITH_LEJP
+/* #undef LWS_WITH_LIBEV */
+/* #undef LWS_WITH_LIBEVENT */
+/* #undef LWS_WITH_LIBUV */
+#define LWS_WITH_LWSAC
+#define LWS_LOGS_TIMESTAMP
+/* #undef LWS_WITH_MBEDTLS */
+/* #undef LWS_WITH_MINIZ */
+#define LWS_WITH_NETWORK
+/* #undef LWS_WITH_NO_LOGS */
+#define LWS_WITH_CLIENT
+#define LWS_WITHOUT_EXTENSIONS
+#define LWS_WITH_SERVER
+/* #undef LWS_WITH_SPAWN */
+/* #undef LWS_WITH_PEER_LIMITS */
+/* #undef LWS_WITH_PLUGINS */
+/* #undef LWS_WITH_POLARSSL */
+#define LWS_WITH_POLL
+/* #undef LWS_WITH_RANGES */
+/* #undef LWS_WITH_SECURE_STREAMS */
+/* #undef LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM */
+/* #undef LWS_WITH_SECURE_STREAMS_PROXY_API */
+/* #undef LWS_WITH_SELFTESTS */
+#define LWS_WITH_SEQUENCER
+/* #undef LWS_WITH_SERVER_STATUS */
+/* #undef LWS_WITH_SMTP */
+/* #undef LWS_WITH_SOCKS5 */
+/* #undef LWS_WITH_STATEFUL_URLDECODE */
+/* #undef LWS_WITH_STATS */
+/* #undef LWS_WITH_STRUCT_SQLITE3 */
+/* #undef LWS_WITH_STRUCT_JSON */
+/* #undef LWS_WITH_SQLITE3 */
+/* #undef LWS_WITH_SYS_NTPCLIENT */
+/* #undef LWS_WITH_SYS_DHCP_CLIENT */
+/* #undef LWS_WITH_THREADPOOL */
+#define LWS_WITH_TLS
+#define LWS_WITH_UDP
+/* #undef LWS_WITH_UNIX_SOCK */
+/* #undef LWS_WITH_ZIP_FOPS */
+/* #undef USE_OLD_CYASSL */
+/* #undef USE_WOLFSSL */
+
+
--- /dev/null
+### build version
+
+- Libwebsockets v4.0-stable
+- OpenSSL 1.1.1i
--- /dev/null
+# Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+SET(VINE_TOOL "vine-tool")
+
+LINK_DIRECTORIES(${CMAKE_BINARY_DIR})
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -Wall -fPIE")
+SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -pie")
+
+INCLUDE_DIRECTORIES(
+ ${CMAKE_SOURCE_DIR}/include
+ ${VINE_LOGGER_PATH}
+ ${fw_name_deps_INCLUDE_DIRS}
+)
+
+FILE(GLOB VINE_TOOL_SRCS *.cpp)
+ADD_EXECUTABLE(${VINE_TOOL} ${VINE_TOOL_SRCS})
+TARGET_LINK_LIBRARIES(${VINE_TOOL}
+ ${TARGET_VINE}
+ ${VINE_LOGGER}
+ ${fw_name_deps_LIBRARIES}
+)
+
+INSTALL(TARGETS ${VINE_TOOL} DESTINATION "${BIN_DIR}")
--- /dev/null
+#include "tool_config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#define STRDUP(a) (a) ? strdup(a) : NULL
+
+static struct {
+ bool reg;
+ bool disc;
+ char *service_name;
+ char *service_type;
+ char *iface_name;
+ char *remote_addr;
+ int addr_family;
+ int port;
+ dp_type_t dp_type;
+ int max_conn;
+ char *topic;
+ char *message;
+ char *file;
+ int interval;
+ char *cacert;
+ char *cert;
+ char *key;
+ char *psk;
+ int verbose;
+ char *log_file;
+} configs = {
+ .reg = false,
+ .disc = false,
+ .service_name = NULL,
+ .service_type = NULL,
+ .iface_name = NULL,
+ .remote_addr = NULL,
+ .addr_family = 0,
+ .port = 0,
+ .dp_type = DP_TYPE_UNKNOWN,
+ .max_conn = 0,
+ .topic = NULL,
+ .message = NULL,
+ .file = NULL,
+ .interval = 0,
+ .cacert = NULL,
+ .cert = NULL,
+ .key = NULL,
+ .psk = NULL,
+ .verbose = false,
+ .log_file = NULL,
+};
+
+void tool_config_set_register(bool val)
+{
+ configs.reg = val;
+}
+
+bool tool_config_get_register()
+{
+ return configs.reg;
+}
+
+void tool_config_set_discovery(bool val)
+{
+ configs.disc = val;
+}
+
+bool tool_config_get_discovery()
+{
+ return configs.disc;
+}
+
+void tool_config_set_service_name(char *val)
+{
+ configs.service_name = STRDUP(val);
+}
+
+const char *tool_config_get_service_name()
+{
+ return (const char *)configs.service_name;
+}
+
+void tool_config_set_service_type(char *val)
+{
+ configs.service_type = STRDUP(val);
+}
+
+const char *tool_config_get_service_type()
+{
+ return (const char *)configs.service_type;
+}
+
+void tool_config_set_iface_name(char *val)
+{
+ configs.iface_name = STRDUP(val);
+}
+
+const char *tool_config_get_iface_name()
+{
+ return (const char *)configs.iface_name;
+}
+
+void tool_config_set_remote_address(char *val)
+{
+ configs.remote_addr = STRDUP(val);
+}
+
+const char *tool_config_get_remote_address()
+{
+ return (const char *)configs.remote_addr;
+}
+
+void tool_config_set_address_family(int val)
+{
+ configs.addr_family = val;
+}
+
+int tool_config_get_address_family()
+{
+ return configs.addr_family;
+}
+
+void tool_config_set_port(int val)
+{
+ configs.port = val;
+}
+
+int tool_config_get_port()
+{
+ return configs.port;
+}
+
+void tool_config_set_dp_type(char *val)
+{
+ if (!val)
+ return;
+
+ if (strcmp(val, "client") == 0)
+ configs.dp_type = DP_TYPE_CLIENT;
+ else if (strcmp(val, "server") == 0)
+ configs.dp_type = DP_TYPE_SERVER;
+ else if (strcmp(val, "pubsub") == 0)
+ configs.dp_type = DP_TYPE_PUBSUB;
+}
+
+dp_type_t tool_config_get_dp_type()
+{
+ return configs.dp_type;
+}
+
+void tool_config_set_max_conn(int val)
+{
+ configs.max_conn = val;
+}
+
+int tool_config_get_max_conn()
+{
+ return configs.max_conn;
+}
+
+void tool_config_set_topic(char *val)
+{
+ configs.topic = STRDUP(val);
+}
+
+const char *tool_config_get_topic()
+{
+ return configs.topic;
+}
+
+void tool_config_set_message(char *val)
+{
+ configs.message = STRDUP(val);
+}
+
+const char *tool_config_get_message()
+{
+ return configs.message;
+}
+
+void tool_config_set_file(char *val)
+{
+ configs.file = STRDUP(val);
+}
+
+const char *tool_config_get_file()
+{
+ return configs.file;
+}
+
+void tool_config_set_interval(int val)
+{
+ configs.interval = val;
+}
+
+int tool_config_get_interval()
+{
+ return configs.interval;
+}
+
+sec_type_t tool_config_get_security_type()
+{
+ if (configs.psk)
+ return SEC_TYPE_PSK_OVER_TLS;
+ else if (configs.cacert || configs.cert)
+ return SEC_TYPE_TLS;
+ else
+ return SEC_TYPE_NONE;
+}
+
+void tool_config_set_cacert(char *val)
+{
+ configs.cacert = STRDUP(val);
+}
+
+const char *tool_config_get_cacert()
+{
+ return configs.cacert;
+}
+
+void tool_config_set_cert(char *val)
+{
+ configs.cert = STRDUP(val);
+}
+
+const char *tool_config_get_cert()
+{
+ return configs.cert;
+}
+
+void tool_config_set_private_key(char *val)
+{
+ configs.key = STRDUP(val);
+}
+
+const char *tool_config_get_private_key()
+{
+ return configs.key;
+}
+
+void tool_config_set_psk(char *val)
+{
+ configs.psk = STRDUP(val);
+}
+
+const char *tool_config_get_psk()
+{
+ return configs.psk;
+}
+
+void tool_config_set_verbose(bool val)
+{
+ configs.verbose = val;
+}
+
+bool tool_config_get_verbose()
+{
+ return configs.verbose;
+}
+
+void tool_config_set_log_file(char *val)
+{
+ configs.log_file = STRDUP(val);
+}
+
+const char *tool_config_get_log_file()
+{
+ return (const char *)configs.log_file;
+}
+
+void tool_config_deinit()
+{
+ free(configs.service_name);
+ free(configs.service_type);
+ free(configs.iface_name);
+ free(configs.remote_addr);
+ free(configs.topic);
+ free(configs.message);
+ free(configs.cacert);
+ free(configs.cert);
+ free(configs.key);
+ free(configs.psk);
+ free(configs.log_file);
+}
--- /dev/null
+typedef enum {
+ DP_TYPE_CLIENT = 0,
+ DP_TYPE_SERVER,
+ DP_TYPE_PUBSUB,
+ DP_TYPE_UNKNOWN,
+} dp_type_t;
+
+typedef enum {
+ SEC_TYPE_NONE = 0,
+ SEC_TYPE_TLS,
+ SEC_TYPE_PSK_OVER_TLS,
+} sec_type_t;
+
+void tool_config_set_register(bool val);
+bool tool_config_get_register();
+void tool_config_set_discovery(bool val);
+bool tool_config_get_discovery();
+void tool_config_set_service_name(char *val);
+const char *tool_config_get_service_name();
+void tool_config_set_service_type(char *val);
+const char *tool_config_get_service_type();
+void tool_config_set_iface_name(char *val);
+const char *tool_config_get_iface_name();
+void tool_config_set_remote_address(char *val);
+const char *tool_config_get_remote_address();
+void tool_config_set_address_family(int val);
+int tool_config_get_address_family();
+void tool_config_set_port(int val);
+int tool_config_get_port();
+void tool_config_set_dp_type(char *val);
+dp_type_t tool_config_get_dp_type();
+void tool_config_set_max_conn(int val);
+int tool_config_get_max_conn();
+void tool_config_set_topic(char *val);
+const char *tool_config_get_topic();
+void tool_config_set_message(char *val);
+const char *tool_config_get_message();
+void tool_config_set_file(char *val);
+const char *tool_config_get_file();
+void tool_config_set_interval(int val);
+int tool_config_get_interval();
+sec_type_t tool_config_get_security_type();
+void tool_config_set_cacert(char *val);
+const char *tool_config_get_cacert();
+void tool_config_set_cert(char *val);
+const char *tool_config_get_cert();
+void tool_config_set_private_key(char *val);
+const char *tool_config_get_private_key();
+void tool_config_set_psk(char *val);
+const char *tool_config_get_psk();
+void tool_config_set_verbose(bool val);
+bool tool_config_get_verbose();
+void tool_config_set_log_file(char *val);
+const char *tool_config_get_log_file();
+void tool_config_deinit();
--- /dev/null
+#include "tool_help.h"
+
+#include <stdio.h>
+
+#define OPT_TYPE_SERVICE_DISCOVERY 1
+#define OPT_TYPE_DATA_PATH 2
+#define OPT_TYPE_SECURITY 3
+#define OPT_TYPE_EXT 4
+#define OPT_TYPE_MAX 99
+
+typedef int opt_type_t;
+
+struct help_msg {
+ opt_type_t type;
+ const char *opt;
+ const char *desc;
+};
+
+// Usage: vine-tool [options...]
+// ------------------------------------------------------------
+// -r, --register Register a service
+// -d, --discovery Discovery services
+// -n, --name <name> Service name
+// -t, --type <type> Service type
+// -i, --iface <iface> Interface name
+// ------------------------------------------------------------
+// -a, --addr <IP> Remote IP
+// -4, --ipv4 IPv4 only (optional)
+// -6, --ipv6 IPv6 (optional)
+// -p, --port <port> Port
+// -D, --dp <DP type> DP Type (client|server|pubsub)
+// -T, --topic <topic> Topic
+// -m, --message <message> Send Message
+// -f, --file <file> Send File
+// -C, --max-conn <conns> Max number of connections
+// ------------------------------------------------------------
+// --cacert <path> CA certificates path
+// --cert <path> Server certificate path
+// --key <path> Private Key
+// --psk <psk> PSK
+//-------------------------------------------------------------
+// -I, --interval <sec> Interval of sending a message (repeated until terminating app)
+// -v, --verbose Print log messages
+// -w <file> Log file
+static const struct help_msg help_msgs[] = {
+ {OPT_TYPE_SERVICE_DISCOVERY, "-r, --register", "Register a service"},
+ {OPT_TYPE_SERVICE_DISCOVERY, "-d, --discovery", "Discovery services"},
+ {OPT_TYPE_SERVICE_DISCOVERY, "-n, --name <name>", "Service name"},
+ {OPT_TYPE_SERVICE_DISCOVERY, "-t, --type <type>", "Service type"},
+ {OPT_TYPE_SERVICE_DISCOVERY, "-i, --iface <iface>", "Interface name"},
+ {OPT_TYPE_DATA_PATH, "-a, --addr <IP>", "Remote IP"},
+ {OPT_TYPE_DATA_PATH, "-4, --ipv4", "IPv4 only (optional)"},
+ {OPT_TYPE_DATA_PATH, "-6, --ipv6", "IPv6 (optional)"},
+ {OPT_TYPE_DATA_PATH, "-p, --port <port>", "Port"},
+ {OPT_TYPE_DATA_PATH, "-D, --dp <DP type>", "DP Type (client|server|pubsub)"},
+ {OPT_TYPE_DATA_PATH, "-C, --max-conn <conns>", "Max number of connections"},
+ {OPT_TYPE_DATA_PATH, "-T, --topic <topic>", "Topic"},
+ {OPT_TYPE_DATA_PATH, "-m, --message <message>", "Send Message"},
+ {OPT_TYPE_DATA_PATH, "-f, --file <file>", "Send File"},
+ {OPT_TYPE_DATA_PATH, "-I, --interval <sec>",
+ "Interval of sending a message\n\t\t\t(repeated until terminating app)"},
+ {OPT_TYPE_SECURITY, "--cacert <path>", "CA certificates path"},
+ {OPT_TYPE_SECURITY, "--key <path>", "Private key path"},
+ {OPT_TYPE_SECURITY, "--cert <path>", "Server certificate path"},
+ {OPT_TYPE_SECURITY, "--psk <psk>", "PSK"},
+ {OPT_TYPE_EXT, "-v, --verbose", "Print log messages"},
+ {OPT_TYPE_EXT, "-w <file>", "Log file"},
+ {OPT_TYPE_EXT, "-h, --help", "Show the help messages"},
+ {OPT_TYPE_MAX, "", ""},
+};
+
+void tool_help_msgs()
+{
+ printf(" Usage: vine-tool [options...]\n");
+ for (int i = 0; help_msgs[i].type != OPT_TYPE_MAX ; i++) {
+ if (i == 0 || help_msgs[i].type != help_msgs[i - 1].type)
+ printf(" --------------------------------------------------\n");
+ printf(" %-22s %s\n", help_msgs[i].opt, help_msgs[i].desc);
+ }
+}
--- /dev/null
+void tool_help_msgs();
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <signal.h>
+
+#include "tool_help.h"
+#include "tool_parse.h"
+#include "tool_run.h"
+
+int interrupt_flag = 0;
+static void interrupt_handler(int signo)
+{
+ interrupt_flag = 1;
+}
+
+int main(int argc, char **argv)
+{
+ signal(SIGINT, interrupt_handler);
+
+ if (argc == 1) {
+ tool_help_msgs();
+ return 0;
+ }
+
+ if (tool_parse_params(argc, argv) == -1) {
+ printf("error.\n");
+ return 0;
+ }
+
+ if (tool_run() == -1) {
+ printf("Failed.\n");
+ return 0;
+ }
+
+ return 0;
+}
--- /dev/null
+#include "tool_parse.h"
+#include "tool_config.h"
+#include "tool_help.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+static struct option options[] = {
+ {"register", no_argument, 0, 'r'},
+ {"discovery", no_argument, 0, 'd'},
+ {"name", required_argument, 0, 'n'},
+ {"type", required_argument, 0, 't'},
+ {"iface", required_argument, 0, 'i'},
+ {"addr", required_argument, 0, 'a'},
+ {"ipv4", no_argument, 0, '4'},
+ {"ipv6", no_argument, 0, '6'},
+ {"port", required_argument, 0, 'p'},
+ {"dp", required_argument, 0, 'D'},
+ {"max-conn", required_argument, 0, 'C'},
+ {"topic", required_argument, 0, 'T'},
+ {"message", required_argument, 0, 'm'},
+ {"file", required_argument, 0, 'f'},
+ {"interval", required_argument, 0, 'I'},
+ {"cacert", required_argument, 0, 0},
+ {"cert", required_argument, 0, 1},
+ {"key", required_argument, 0, 2},
+ {"psk", required_argument, 0, 3},
+ {"verbose", no_argument, 0, 'v'},
+ {"help", no_argument, 0, 'h'},
+ {0, 0, 0, 0}
+};
+
+int tool_parse_params(int argc, char **argv)
+{
+ int c;
+ int error = 0;
+ int val = 0;
+
+ while (!error) {
+ int idx = 0;
+ c = getopt_long(argc, argv, "rdn:t:i:a:46p:D:T:C:m:f:I:vw:h", options, &idx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'r':
+ tool_config_set_register(true);
+ break;
+ case 'd':
+ tool_config_set_discovery(true);
+ break;
+ case 'n':
+ tool_config_set_service_name(optarg);
+ break;
+ case 't':
+ tool_config_set_service_type(optarg);
+ break;
+ case 'i':
+ tool_config_set_iface_name(optarg);
+ break;
+ case 'a':
+ tool_config_set_remote_address(optarg);
+ break;
+ case '4':
+ tool_config_set_address_family(4);
+ break;
+ case '6':
+ tool_config_set_address_family(6);
+ break;
+ case 'p':
+ val = atoi(optarg);
+ tool_config_set_port(val);
+ break;
+ case 'D':
+ tool_config_set_dp_type(optarg);
+ break;
+ case 'C':
+ val = atoi(optarg);
+ tool_config_set_max_conn(val);
+ break;
+ case 'T':
+ tool_config_set_topic(optarg);
+ break;
+ case 'm':
+ tool_config_set_message(optarg);
+ break;
+ case 'f':
+ tool_config_set_file(optarg);
+ break;
+ case 'I':
+ val = atoi(optarg);
+ tool_config_set_interval(val);
+ break;
+ case 0:
+ tool_config_set_cacert(optarg);
+ break;
+ case 1:
+ tool_config_set_cert(optarg);
+ break;
+ case 2:
+ tool_config_set_private_key(optarg);
+ break;
+ case 3:
+ tool_config_set_psk(optarg);
+ break;
+ case 'v':
+ tool_config_set_verbose(true);
+ break;
+ case 'w':
+ tool_config_set_log_file(optarg);
+ break;
+ case 'h':
+ tool_help_msgs();
+ break;
+ default:
+ printf("Invalid argument.\n");
+ tool_help_msgs();
+ error = 1;
+ break;
+ }
+ }
+
+ return error ? -1 : 0;
+}
--- /dev/null
+int tool_parse_params(int argc, char **argv);
--- /dev/null
+#include "tool_run.h"
+
+#include "tool_config.h"
+
+#include <vine.h>
+#include <vine-log.h>
+#include <sys/epoll.h>
+#include <sys/syscall.h>
+#include <sys/timerfd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define RESET_COLOR "\e[m"
+#define MAKE_RED "\e[31m"
+#define MAKE_GREEN "\e[32m"
+
+#define MAX_EVENTS 10
+#define MAX_READ_LEN 1024
+
+extern int interrupt_flag;
+
+static int epollfd = 0;
+static int timerfd = 0;
+static FILE *log_file = NULL;
+
+static struct {
+ vine_session_h session;
+ vine_service_h service;
+ vine_dp_h dp;
+ vine_security_h security;
+ const char *type;
+ const char *iface;
+ vine_dp_type_e dp_type;
+ int max_conn;
+ const char *msg;
+ const char *file;
+ int interval;
+ bool is_reg;
+ bool is_disc;
+ bool is_dp;
+ bool is_sec;
+} vine_configs = {
+ .session = NULL,
+ .service = NULL,
+ .dp = NULL,
+ .security = NULL,
+ .type = NULL,
+ .iface = NULL,
+ .dp_type = VINE_DP_TYPE_UNKNOWN,
+ .max_conn = 0,
+ .msg = NULL,
+ .file = NULL,
+ .interval = 0,
+ .is_reg = false,
+ .is_disc = false,
+ .is_dp = false,
+ .is_sec = false,
+};
+
+static int send_message(vine_dp_h dp);
+static void _stop_message_timer();
+
+static void __print_received_data(unsigned char *buf, size_t len)
+{
+ for (int i = 0; i < (int)len; i++)
+ printf("%c", buf[i]);
+ printf("\n");
+}
+
+static void __received_cb(vine_dp_h dp, size_t received_len, void *user_data)
+{
+ unsigned char buf[MAX_READ_LEN] = {0, };
+ size_t bytes = 0;
+
+ printf("Message is received. %zd bytes.\n", received_len);
+ do {
+ vine_dp_recv(dp, buf, MAX_READ_LEN, &bytes);
+ __print_received_data(buf, bytes);
+ received_len -= bytes;
+ } while (received_len > 0);
+}
+
+static void __terminated_cb(vine_dp_h dp, void *user_data)
+{
+ printf("peer is terminated.\n");
+ _stop_message_timer();
+}
+
+static void __opened_cb(vine_dp_h dp, vine_error_e result, void *user_data)
+{
+ printf("DP %s.\n", result == VINE_ERROR_NONE ? "is opened" : "open failure");
+ vine_dp_set_received_cb(dp, __received_cb, NULL);
+ vine_dp_set_terminated_cb(dp, __terminated_cb, NULL);
+
+ if (result == VINE_ERROR_NONE
+ && vine_configs.dp_type != VINE_DP_TYPE_SERVER
+ && (vine_configs.msg || vine_configs.file))
+ send_message(dp);
+}
+
+static void __accepted_cb(vine_dp_h dp, vine_dp_h accepted_dp, void *user_data)
+{
+ printf("client is accepted.\n");
+ vine_dp_set_received_cb(accepted_dp, __received_cb, NULL);
+ vine_dp_set_terminated_cb(accepted_dp, __terminated_cb, NULL);
+
+ if (vine_configs.msg || vine_configs.file)
+ send_message(accepted_dp);
+}
+
+static void __registered_cb(vine_session_h session,
+ const char *service_name, vine_error_e error, void *user_data)
+{
+ printf("Service[%s] %s.\n", service_name,
+ error == VINE_ERROR_NONE ? "registered" : "registration failure");
+}
+
+static bool __print_attr(const char *key, const char *value, void *user_data)
+{
+ printf("\t > Attributes: %s=%s\n", key, value);
+ return true;
+}
+
+static void __ip_resolved_cb(vine_session_h session, vine_service_h service,
+ const char *ip, vine_address_family_e address_family,
+ void *user_data)
+{
+ char *service_type;
+ char *service_name;
+ int port;
+
+ printf("Service discovered.\n");
+ vine_service_get_type(service, &service_type);
+ vine_service_get_name(service, &service_name);
+ vine_service_get_port(service, &port);
+
+ printf("\t > Service Type: %s\n", service_type);
+ printf("\t > Service Name: %s\n", service_name);
+ printf("\t > IP Address: %s\n", ip);
+ printf("\t > Port: %d\n", port);
+ vine_service_foreach_attribute(service, __print_attr, NULL);
+ printf("\n");
+ fflush(stdout);
+}
+
+static void __discovered_cb(vine_session_h session, vine_service_h service,
+ vine_service_state_e state, void *user_data)
+{
+ printf("available[%d]\n", state);
+ vine_session_set_ip_resolved_cb(session, service, __ip_resolved_cb, NULL);
+}
+
+static vine_dp_type_e _convert_dp_type(dp_type_t type)
+{
+ switch (type) {
+ case DP_TYPE_CLIENT:
+ return VINE_DP_TYPE_CLIENT;
+ case DP_TYPE_SERVER:
+ return VINE_DP_TYPE_SERVER;
+ case DP_TYPE_PUBSUB:
+ return VINE_DP_TYPE_PUBSUB;
+ case DP_TYPE_UNKNOWN:
+ default:
+ return VINE_DP_TYPE_UNKNOWN;
+ }
+}
+
+static vine_security_type_e _convert_sec_type(sec_type_t type)
+{
+ switch (type) {
+ case SEC_TYPE_NONE:
+ return VINE_SECURITY_TYPE_NONE;
+ case SEC_TYPE_TLS:
+ return VINE_SECURITY_TYPE_TLS;
+ case SEC_TYPE_PSK_OVER_TLS:
+ return VINE_SECURITY_TYPE_PSK_OVER_TLS;
+ default:
+ return VINE_SECURITY_TYPE_NONE;
+ }
+}
+
+static vine_address_family_e _convert_addr_family(int family)
+{
+ switch (family) {
+ case 0:
+ return VINE_ADDRESS_FAMILY_DEFAULT;
+ case 4:
+ return VINE_ADDRESS_FAMILY_IPV4;
+ case 6:
+ return VINE_ADDRESS_FAMILY_IPV6;
+ default:
+ return VINE_ADDRESS_FAMILY_DEFAULT;
+ }
+}
+
+static void _start_message_timer(vine_dp_h dp, int sec)
+{
+ if (sec <= 0)
+ return;
+
+ timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
+ if (timerfd == -1)
+ return;
+
+ struct itimerspec value;
+ value.it_interval.tv_sec = 0;
+ value.it_interval.tv_nsec = 0;
+ value.it_value.tv_sec = sec;
+ value.it_value.tv_nsec = 0;
+ if (timerfd_settime(timerfd, 0, &value, NULL) != 0) {
+ close(timerfd);
+ return;
+ }
+
+ struct epoll_event ev;
+ ev.events = EPOLLIN;
+ ev.data.ptr = (void *)dp;
+ epoll_ctl(epollfd, EPOLL_CTL_ADD, timerfd, &ev);
+}
+
+static void _stop_message_timer()
+{
+ if (timerfd <= 0)
+ return;
+
+ epoll_ctl(epollfd, EPOLL_CTL_DEL, timerfd, NULL);
+ close(timerfd);
+ timerfd = 0;
+}
+
+static void _message_timer_handler(void *user_data)
+{
+ vine_dp_h dp = (vine_dp_h)user_data;
+ _stop_message_timer();
+ send_message(dp);
+}
+
+static int _send_message_from_file(vine_dp_h dp)
+{
+ unsigned char *buf;
+ size_t size, count = 0;
+ int ret;
+ FILE *file = fopen(vine_configs.file, "r");
+
+ if (file == NULL)
+ return -1;
+
+ fseek(file, 0, SEEK_END);
+
+ size = ftell(file);
+ buf = (unsigned char *)calloc(sizeof(unsigned char), size + 1);
+
+ fseek(file, 0, SEEK_SET);
+ count = fread(buf, sizeof(unsigned char), size, file);
+ fclose(file);
+
+ ret = vine_dp_send(dp, buf, count);
+ free(buf);
+
+ printf("Sent total %zd bytes.\n", count);
+
+ return ret;
+}
+
+static int _send_message(vine_dp_h dp)
+{
+ size_t len = strlen(vine_configs.msg);
+ if (len == 0)
+ return -1;
+
+ unsigned char *buf = (unsigned char *)calloc(len, sizeof(unsigned char));
+ memcpy(buf, vine_configs.msg, len);
+
+ int ret = vine_dp_send(dp, buf, len);
+ _start_message_timer(dp, vine_configs.interval);
+ return ret;
+}
+
+static int send_message(vine_dp_h dp)
+{
+ int ret;
+ if (vine_configs.file)
+ ret = _send_message_from_file(dp);
+ else if (vine_configs.msg)
+ ret = _send_message(dp);
+ return ret != VINE_ERROR_NONE ? -1 : 0;
+}
+
+static int init()
+{
+ int ret = vine_initialize();
+ if (ret != VINE_ERROR_NONE)
+ return -1;
+
+ ret = vine_session_create(&vine_configs.session);
+ if (ret != VINE_ERROR_NONE)
+ return -1;
+
+ return 0;
+}
+
+static void deinit()
+{
+ tool_config_deinit();
+
+ if (timerfd)
+ _stop_message_timer();
+
+ if (log_file) {
+ fclose(log_file);
+ log_file = NULL;
+ }
+
+ if (vine_configs.is_reg)
+ vine_session_unregister(vine_configs.session);
+
+ if (vine_configs.is_disc)
+ vine_session_stop_discovery(vine_configs.session);
+
+ if (vine_configs.is_dp)
+ vine_dp_close(vine_configs.dp);
+
+ if (vine_configs.security)
+ vine_security_destroy(vine_configs.security);
+
+ if (vine_configs.dp)
+ vine_dp_destroy(vine_configs.dp);
+
+ if (vine_configs.service)
+ vine_service_destroy(vine_configs.service);
+
+ if (vine_configs.session)
+ vine_session_destroy(vine_configs.session);
+
+ vine_deinitialize();
+}
+
+static void _logger(int log_level, const char *log)
+{
+ if (log_file) {
+ fprintf(log_file, "%s\n", log);
+ return;
+ }
+
+ struct timespec ts;
+ if(clock_gettime(CLOCK_REALTIME, &ts) == -1 )
+ return;
+
+ printf("[%ld.%03ld][T%5u] ",
+ ts.tv_sec, ts.tv_nsec / 1000000, (unsigned int)syscall(__NR_gettid));
+ switch (log_level) {
+ case VINE_LOG_DEBUG:
+ printf("DEBUG: ");
+ break;
+ case VINE_LOG_INFO:
+ printf("INFO: ");
+ break;
+ case VINE_LOG_ERROR:
+ printf(MAKE_RED "ERROR: ");
+ break;
+ }
+ printf("%s" RESET_COLOR "\n", log);
+}
+
+static void debug_on()
+{
+ if (!tool_config_get_verbose())
+ return;
+
+ vine_set_log_level(VINE_LOG_ERROR | VINE_LOG_INFO | VINE_LOG_DEBUG);
+
+ const char *log_file_path = tool_config_get_log_file();
+ if (log_file_path) {
+ log_file = fopen(log_file_path, "w+");
+ }
+ vine_set_logger(_logger);
+}
+
+static void _set_register_info()
+{
+ vine_service_create(&vine_configs.service);
+ vine_service_set_name(vine_configs.service, tool_config_get_service_name());
+ vine_service_set_type(vine_configs.service, tool_config_get_service_type());
+ vine_service_set_port(vine_configs.service, tool_config_get_port());
+ vine_session_set_registered_cb(vine_configs.session, __registered_cb, NULL);
+}
+
+static void _set_discovery_info()
+{
+ vine_configs.type = tool_config_get_service_type();
+ vine_configs.iface = tool_config_get_iface_name();
+ vine_session_set_discovered_cb(vine_configs.session, __discovered_cb, NULL);
+}
+
+static void _set_dp_info(vine_dp_type_e type)
+{
+ vine_dp_create(vine_configs.session, type, &vine_configs.dp);
+ vine_dp_set_port(vine_configs.dp, tool_config_get_port());
+
+ vine_address_family_e addr_family = _convert_addr_family(tool_config_get_address_family());
+ vine_dp_set_address_family(vine_configs.dp, addr_family);
+ vine_dp_set_max_connections(vine_configs.dp, tool_config_get_max_conn());
+
+ if (type == VINE_DP_TYPE_CLIENT) {
+ vine_dp_set_remote_ip(vine_configs.dp, addr_family,
+ tool_config_get_remote_address());
+ vine_dp_set_received_cb(vine_configs.dp, __received_cb, NULL);
+ }
+
+ if (type == VINE_DP_TYPE_SERVER)
+ vine_dp_set_accepted_cb(vine_configs.dp, __accepted_cb, NULL);
+
+ if (type == VINE_DP_TYPE_PUBSUB)
+ vine_dp_set_topic(vine_configs.dp, tool_config_get_topic());
+
+ vine_dp_set_iface_name(vine_configs.dp, tool_config_get_iface_name());
+ vine_configs.msg = tool_config_get_message();
+ vine_configs.file = tool_config_get_file();
+ vine_configs.interval = tool_config_get_interval();
+ vine_dp_set_security(vine_configs.dp, vine_configs.security);
+}
+
+static void _set_security_info(vine_security_type_e type)
+{
+ vine_security_create(&vine_configs.security);
+ vine_security_set_type(vine_configs.security, type);
+ vine_security_set_ca_path(vine_configs.security, tool_config_get_cacert());
+ vine_security_set_cert_path(vine_configs.security, tool_config_get_cert());
+ vine_security_set_private_key(vine_configs.security, tool_config_get_private_key());
+ vine_security_set_psk(vine_configs.security, tool_config_get_psk());
+ vine_security_set_tls_version(vine_configs.security, VINE_SECURITY_TLS_VERSION_1_3);
+ vine_security_set_verification_flags(vine_configs.security,
+ VINE_SECURITY_VERIFICATION_FLAG_ALLOW_SELF_SIGNED
+ | VINE_SECURITY_VERIFICATION_FLAG_SKIP_HOST_NAME_CHECK); // for local test
+}
+
+static void set_vine_configs()
+{
+ vine_configs.is_reg = tool_config_get_register();
+ if (vine_configs.is_reg)
+ _set_register_info();
+
+ vine_configs.is_disc = tool_config_get_discovery();
+ if (vine_configs.is_disc)
+ _set_discovery_info();
+
+ vine_security_type_e sec_type = _convert_sec_type(tool_config_get_security_type());
+ vine_configs.is_sec = sec_type != VINE_SECURITY_TYPE_NONE ? true : false;
+ if (vine_configs.is_sec)
+ _set_security_info(sec_type);
+
+ vine_configs.dp_type = _convert_dp_type(tool_config_get_dp_type());
+ vine_configs.is_dp = vine_configs.dp_type != VINE_DP_TYPE_UNKNOWN ? true : false;
+ if (vine_configs.is_dp)
+ _set_dp_info(vine_configs.dp_type);
+}
+
+static void _event_handler(vine_session_h session)
+{
+ vine_session_process_event(session);
+}
+
+static void run_event_loop(vine_session_h session)
+{
+ int fd;
+ int ret = vine_session_get_event_fd(session, &fd);
+ if (ret != VINE_ERROR_NONE)
+ return;
+
+ epollfd = epoll_create1(0);
+ if (epollfd == -1) {
+ printf("Fail to create epoll fd %d\n", errno);
+ exit(1);
+ }
+
+ struct epoll_event ev, events[MAX_EVENTS];
+ ev.events = EPOLLIN;
+ ev.data.ptr = NULL;
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+ printf("Fail to add an epoll event for fd %d: %d\n", fd, errno);
+ exit(1);
+ }
+
+ while (!interrupt_flag) {
+ int n = epoll_wait(epollfd, events, MAX_EVENTS, 0);
+ if (n == -1) {
+ printf("error: epoll_wait %d\n", errno);
+ exit(1);
+ }
+
+ for (int i = 0; i < n; ++i) {
+ if (events[i].data.ptr)
+ _message_timer_handler(events[i].data.ptr);
+ else
+ _event_handler(session);
+ }
+ }
+ close(epollfd);
+}
+
+int tool_run()
+{
+ int ret = 0;
+
+ debug_on();
+
+ if (init() < 0)
+ goto DONE;
+
+ set_vine_configs();
+
+ if (vine_configs.is_reg) {
+ ret = vine_session_register(vine_configs.session,
+ vine_configs.service, vine_configs.iface);
+ if (ret != VINE_ERROR_NONE)
+ goto DONE;
+ }
+
+ if (vine_configs.is_disc) {
+ ret = vine_session_start_discovery(vine_configs.session,
+ vine_configs.type, vine_configs.iface);
+ if (ret != VINE_ERROR_NONE)
+ goto DONE;
+ }
+
+ if (vine_configs.is_dp) {
+ ret = vine_dp_open(vine_configs.dp, __opened_cb, NULL);
+ if (ret != VINE_ERROR_NONE)
+ goto DONE;
+ }
+
+ run_event_loop(vine_configs.session);
+
+DONE:
+ deinit();
+ return ret;
+}
--- /dev/null
+int tool_run();