Merge branch 'ony-dev' into v15-dev
This commit is contained in:
commit
a78e458a43
16
.project
16
.project
|
@ -5,6 +5,12 @@
|
||||||
<projects>
|
<projects>
|
||||||
</projects>
|
</projects>
|
||||||
<buildSpec>
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
|
||||||
|
<triggers>clean,full,incremental,</triggers>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
<buildCommand>
|
<buildCommand>
|
||||||
<name>org.eclipse.wst.jsdt.core.javascriptValidator</name>
|
<name>org.eclipse.wst.jsdt.core.javascriptValidator</name>
|
||||||
<arguments>
|
<arguments>
|
||||||
|
@ -30,10 +36,20 @@
|
||||||
<arguments>
|
<arguments>
|
||||||
</arguments>
|
</arguments>
|
||||||
</buildCommand>
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
|
||||||
|
<triggers>full,incremental,</triggers>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
</buildSpec>
|
</buildSpec>
|
||||||
<natures>
|
<natures>
|
||||||
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
|
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
|
||||||
|
<nature>org.eclipse.cdt.core.cnature</nature>
|
||||||
|
<nature>org.eclipse.cdt.core.ccnature</nature>
|
||||||
|
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
|
||||||
|
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
|
||||||
</natures>
|
</natures>
|
||||||
</projectDescription>
|
</projectDescription>
|
||||||
|
|
|
@ -8,16 +8,12 @@
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="9" android:maxSdkVersion="22" android:targetSdkVersion="22"/>
|
<uses-sdk android:minSdkVersion="9" android:maxSdkVersion="22" android:targetSdkVersion="22"/>
|
||||||
<permission android:name="org.torproject.android.MANAGE_TOR" android:label="@string/permission_manage_tor_label" android:description="@string/permission_manage_tor_description" android:protectionLevel="signature"></permission>
|
<permission android:name="org.torproject.android.MANAGE_TOR" android:label="@string/permission_manage_tor_label" android:description="@string/permission_manage_tor_description" android:protectionLevel="signature"></permission>
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>
|
<uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>
|
||||||
<uses-permission android:name="org.torproject.android.MANAGE_TOR"/>
|
<uses-permission android:name="org.torproject.android.MANAGE_TOR"/>
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
|
|
||||||
|
|
||||||
<application android:name=".OrbotApp" android:icon="@drawable/ic_launcher"
|
<application android:name="org.sandroproxy.ony.OrbotApp" android:icon="@drawable/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:description="@string/app_description"
|
android:description="@string/app_description"
|
||||||
android:configChanges="locale|orientation|screenSize"
|
android:configChanges="locale|orientation|screenSize"
|
||||||
|
|
|
@ -16,14 +16,14 @@ EXTERNAL_ROOT := $(shell pwd)
|
||||||
# user building this will have to manually set NDK_PROCESSOR or NDK_TOOLCHAIN.
|
# user building this will have to manually set NDK_PROCESSOR or NDK_TOOLCHAIN.
|
||||||
CPU := $(shell uname -m)
|
CPU := $(shell uname -m)
|
||||||
ifeq ($(CPU),x86_64)
|
ifeq ($(CPU),x86_64)
|
||||||
NDK_PROCESSOR=x86_64
|
NDK_PROCESSOR=x86
|
||||||
else
|
else
|
||||||
NDK_PROCESSOR=x86
|
NDK_PROCESSOR=x86
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Android NDK setup
|
# Android NDK setup
|
||||||
NDK_BASE ?= /opt/android-ndk
|
NDK_BASE ?= /opt/android-ndk
|
||||||
NDK_PLATFORM_LEVEL ?= 9
|
NDK_PLATFORM_LEVEL ?= 19
|
||||||
NDK_ABI ?= arm
|
NDK_ABI ?= arm
|
||||||
NDK_TOOLCHAIN_VERSION=4.8
|
NDK_TOOLCHAIN_VERSION=4.8
|
||||||
NDK_SYSROOT=$(NDK_BASE)/platforms/android-$(NDK_PLATFORM_LEVEL)/arch-$(NDK_ABI)
|
NDK_SYSROOT=$(NDK_BASE)/platforms/android-$(NDK_PLATFORM_LEVEL)/arch-$(NDK_ABI)
|
||||||
|
@ -52,7 +52,7 @@ STRIP := $(NDK_TOOLCHAIN_BASE)/bin/$(HOST)-strip \
|
||||||
--strip-unneeded -R .note -R .comment
|
--strip-unneeded -R .note -R .comment
|
||||||
|
|
||||||
# PIEFLAGS for SDK 16/Android L must be set to -fPIE -pie
|
# PIEFLAGS for SDK 16/Android L must be set to -fPIE -pie
|
||||||
PIEFLAGS? =
|
PIEFLAGS = -fPIE -pie
|
||||||
CFLAGS = -DANDROID $(TARGET_CFLAGS) $(PIEFLAGS)
|
CFLAGS = -DANDROID $(TARGET_CFLAGS) $(PIEFLAGS)
|
||||||
LDFLAGS = -llog $(TARGET_LDFLAGS) $(PIEFLAGS)
|
LDFLAGS = -llog $(TARGET_LDFLAGS) $(PIEFLAGS)
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
<classpath>
|
<classpath>
|
||||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||||
<classpathentry kind="src" path="src"/>
|
|
||||||
<classpathentry kind="src" path="gen"/>
|
|
||||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||||
<classpathentry exported="true" kind="lib" path="libs/android-support-v4.jar"/>
|
<classpathentry exported="true" kind="lib" path="libs/android-support-v4.jar"/>
|
||||||
<classpathentry exported="true" kind="lib" path="libs/android-support-v7-appcompat.jar"/>
|
<classpathentry exported="true" kind="lib" path="libs/android-support-v7-appcompat.jar"/>
|
||||||
|
<classpathentry kind="src" path="src"/>
|
||||||
|
<classpathentry kind="src" path="gen"/>
|
||||||
<classpathentry kind="output" path="bin/classes"/>
|
<classpathentry kind="output" path="bin/classes"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
LOCAL_PATH := $(call my-dir)
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_MODULE := tun2socks
|
||||||
|
|
||||||
|
LOCAL_CFLAGS := -std=gnu99
|
||||||
|
LOCAL_CFLAGS += -DBADVPN_THREAD_SAFE=0 -DBADVPN_LINUX -DBADVPN_BREACTOR_BADVPN -D_GNU_SOURCE
|
||||||
|
LOCAL_CFLAGS += -DBADVPN_USE_SELFPIPE -DBADVPN_USE_EPOLL
|
||||||
|
LOCAL_CFLAGS += -DBADVPN_LITTLE_ENDIAN
|
||||||
|
LOCAL_CFLAGS += -DPSIPHON
|
||||||
|
|
||||||
|
LOCAL_C_INCLUDES:= \
|
||||||
|
$(LOCAL_PATH) \
|
||||||
|
$(LOCAL_PATH)/lwip/src/include/ipv4 \
|
||||||
|
$(LOCAL_PATH)/lwip/src/include/ipv6 \
|
||||||
|
$(LOCAL_PATH)/lwip/src/include \
|
||||||
|
$(LOCAL_PATH)/lwip/custom
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := \
|
||||||
|
base/BLog_syslog.c \
|
||||||
|
system/BReactor_badvpn.c \
|
||||||
|
system/BSignal.c \
|
||||||
|
system/BConnection_unix.c \
|
||||||
|
system/BTime.c \
|
||||||
|
system/BUnixSignal.c \
|
||||||
|
system/BNetwork.c \
|
||||||
|
flow/StreamRecvInterface.c \
|
||||||
|
flow/PacketRecvInterface.c \
|
||||||
|
flow/PacketPassInterface.c \
|
||||||
|
flow/StreamPassInterface.c \
|
||||||
|
flow/SinglePacketBuffer.c \
|
||||||
|
flow/BufferWriter.c \
|
||||||
|
flow/PacketBuffer.c \
|
||||||
|
flow/PacketStreamSender.c \
|
||||||
|
flow/PacketPassConnector.c \
|
||||||
|
flow/PacketProtoFlow.c \
|
||||||
|
flow/PacketPassFairQueue.c \
|
||||||
|
flow/PacketProtoEncoder.c \
|
||||||
|
flow/PacketProtoDecoder.c \
|
||||||
|
socksclient/BSocksClient.c \
|
||||||
|
tuntap/BTap.c \
|
||||||
|
lwip/src/core/timers.c \
|
||||||
|
lwip/src/core/udp.c \
|
||||||
|
lwip/src/core/memp.c \
|
||||||
|
lwip/src/core/init.c \
|
||||||
|
lwip/src/core/pbuf.c \
|
||||||
|
lwip/src/core/tcp.c \
|
||||||
|
lwip/src/core/tcp_out.c \
|
||||||
|
lwip/src/core/netif.c \
|
||||||
|
lwip/src/core/def.c \
|
||||||
|
lwip/src/core/mem.c \
|
||||||
|
lwip/src/core/tcp_in.c \
|
||||||
|
lwip/src/core/stats.c \
|
||||||
|
lwip/src/core/inet_chksum.c \
|
||||||
|
lwip/src/core/ipv4/icmp.c \
|
||||||
|
lwip/src/core/ipv4/ip4.c \
|
||||||
|
lwip/src/core/ipv4/ip4_addr.c \
|
||||||
|
lwip/src/core/ipv4/ip_frag.c \
|
||||||
|
lwip/src/core/ipv6/ip6.c \
|
||||||
|
lwip/src/core/ipv6/nd6.c \
|
||||||
|
lwip/src/core/ipv6/icmp6.c \
|
||||||
|
lwip/src/core/ipv6/ip6_addr.c \
|
||||||
|
lwip/src/core/ipv6/ip6_frag.c \
|
||||||
|
lwip/custom/sys.c \
|
||||||
|
tun2socks/tun2socks.c \
|
||||||
|
base/DebugObject.c \
|
||||||
|
base/BLog.c \
|
||||||
|
base/BPending.c \
|
||||||
|
flowextra/PacketPassInactivityMonitor.c \
|
||||||
|
tun2socks/SocksUdpGwClient.c \
|
||||||
|
udpgw_client/UdpGwClient.c
|
||||||
|
|
||||||
|
include $(BUILD_SHARED_LIBRARY)
|
||||||
|
|
|
@ -0,0 +1,408 @@
|
||||||
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
project(BADVPN C)
|
||||||
|
|
||||||
|
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")
|
||||||
|
|
||||||
|
include(TestBigEndian)
|
||||||
|
include(CheckIncludeFiles)
|
||||||
|
include(CheckSymbolExists)
|
||||||
|
include(CheckTypeSize)
|
||||||
|
|
||||||
|
set(BUILD_COMPONENTS)
|
||||||
|
|
||||||
|
macro (build_switch name text default)
|
||||||
|
if (BUILD_NOTHING_BY_DEFAULT)
|
||||||
|
option(BUILD_${name} "${text}" OFF)
|
||||||
|
else ()
|
||||||
|
option(BUILD_${name} "${text}" "${default}")
|
||||||
|
endif ()
|
||||||
|
list(APPEND BUILD_COMPONENTS "${name}")
|
||||||
|
endmacro ()
|
||||||
|
|
||||||
|
# detect Emscripten
|
||||||
|
if (CMAKE_C_COMPILER MATCHES "/emcc$")
|
||||||
|
set(EMSCRIPTEN ON)
|
||||||
|
else ()
|
||||||
|
set(EMSCRIPTEN OFF)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (EMSCRIPTEN)
|
||||||
|
set(ON_IF_NOT_EMSCRIPTEN OFF)
|
||||||
|
else ()
|
||||||
|
set(ON_IF_NOT_EMSCRIPTEN ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND NOT EMSCRIPTEN)
|
||||||
|
set(ON_IF_LINUX ON)
|
||||||
|
else ()
|
||||||
|
set(ON_IF_LINUX OFF)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR EMSCRIPTEN)
|
||||||
|
set(ON_IF_LINUX_OR_EMSCRIPTEN ON)
|
||||||
|
else ()
|
||||||
|
set(ON_IF_LINUX_OR_EMSCRIPTEN OFF)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# define build defaults
|
||||||
|
build_switch(EXAMPLES "build example programs" ON)
|
||||||
|
build_switch(TESTS "build some other example programs" ON)
|
||||||
|
build_switch(SERVER "build badvpn-server" ${ON_IF_NOT_EMSCRIPTEN})
|
||||||
|
build_switch(CLIENT "build badvpn-client" ${ON_IF_NOT_EMSCRIPTEN})
|
||||||
|
build_switch(FLOODER "build badvpn-flooder" ${ON_IF_NOT_EMSCRIPTEN})
|
||||||
|
build_switch(TUN2SOCKS "build badvpn-tun2socks" ${ON_IF_NOT_EMSCRIPTEN})
|
||||||
|
build_switch(UDPGW "build badvpn-udpgw" ${ON_IF_NOT_EMSCRIPTEN})
|
||||||
|
build_switch(NCD "build badvpn-ncd" ${ON_IF_LINUX_OR_EMSCRIPTEN})
|
||||||
|
build_switch(TUNCTL "build badvpn-tunctl" ${ON_IF_LINUX})
|
||||||
|
build_switch(DOSTEST "build dostest-server and dostest-attacker" OFF)
|
||||||
|
|
||||||
|
if (BUILD_NCD AND NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux"))
|
||||||
|
message(FATAL_ERROR "NCD is only available on Linux")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (BUILD_CLIENT OR BUILD_SERVER)
|
||||||
|
find_package(OpenSSL REQUIRED)
|
||||||
|
set(LIBCRYPTO_INCLUDE_DIRS "${OpenSSL_INCLUDE_DIRS}")
|
||||||
|
set(LIBCRYPTO_LIBRARY_DIRS "${OpenSSL_LIBRARY_DIRS}")
|
||||||
|
set(LIBCRYPTO_LIBRARIES "${OpenSSL_LIBRARIES}")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (BUILD_SERVER OR BUILD_CLIENT OR BUILD_FLOODER)
|
||||||
|
find_package(NSPR REQUIRED)
|
||||||
|
find_package(NSS REQUIRED)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# choose reactor
|
||||||
|
if (DEFINED BREACTOR_BACKEND)
|
||||||
|
if (NOT (BREACTOR_BACKEND STREQUAL "badvpn" OR BREACTOR_BACKEND STREQUAL "glib"))
|
||||||
|
message(FATAL_ERROR "unknown reactor backend specified")
|
||||||
|
endif ()
|
||||||
|
else ()
|
||||||
|
if (EMSCRIPTEN)
|
||||||
|
set(BREACTOR_BACKEND "emscripten")
|
||||||
|
else ()
|
||||||
|
set(BREACTOR_BACKEND "badvpn")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (BREACTOR_BACKEND STREQUAL "badvpn")
|
||||||
|
add_definitions(-DBADVPN_BREACTOR_BADVPN)
|
||||||
|
elseif (BREACTOR_BACKEND STREQUAL "glib")
|
||||||
|
if (NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux"))
|
||||||
|
message(FATAL_ERROR "GLib reactor backend is only available on Linux")
|
||||||
|
endif ()
|
||||||
|
find_package(GLIB2 REQUIRED)
|
||||||
|
add_definitions(-DBADVPN_BREACTOR_GLIB)
|
||||||
|
elseif (BREACTOR_BACKEND STREQUAL "emscripten")
|
||||||
|
add_definitions(-DBADVPN_BREACTOR_EMSCRIPTEN)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
${LIBCRYPTO_INCLUDE_DIRS}
|
||||||
|
${NSPR_INCLUDE_DIRS}
|
||||||
|
${NSS_INCLUDE_DIRS}
|
||||||
|
${GLIB2_INCLUDE_DIR}
|
||||||
|
lwip/custom
|
||||||
|
lwip/src/include
|
||||||
|
lwip/src/include/ipv4
|
||||||
|
lwip/src/include/ipv6
|
||||||
|
)
|
||||||
|
|
||||||
|
link_directories(
|
||||||
|
${LIBCRYPTO_LIBRARY_DIRS}
|
||||||
|
${NSPR_LIBRARY_DIRS}
|
||||||
|
${NSS_LIBRARY_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
test_big_endian(BIG_ENDIAN)
|
||||||
|
|
||||||
|
check_type_size(int INT_SIZE)
|
||||||
|
if (NOT (INT_SIZE GREATER "3"))
|
||||||
|
message(FATAL_ERROR "int must be at least 32 bits")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
check_type_size(size_t SIZE_SIZE)
|
||||||
|
if (NOT (SIZE_SIZE GREATER INT_SIZE OR SIZE_SIZE EQUAL INT_SIZE))
|
||||||
|
message(FATAL_ERROR "size_t must be greater or equal than int")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
add_definitions(/TP -D_CRT_SECURE_NO_WARNINGS /wd4065 /wd4018 /wd4533 /wd4244 /wd4102)
|
||||||
|
else ()
|
||||||
|
add_definitions(-std=gnu99 -Wall -Wno-unused-value -Wno-parentheses -Wno-switch -Wredundant-decls)
|
||||||
|
|
||||||
|
if (NOT CMAKE_C_COMPILER_ID STREQUAL "PathScale")
|
||||||
|
add_definitions(-Werror=implicit-function-declaration -Wno-switch-enum -Wno-unused-function
|
||||||
|
-Wstrict-aliasing)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (CMAKE_C_COMPILER_ID MATCHES "^Clang")
|
||||||
|
add_definitions(-Wno-initializer-overrides -Wno-tautological-constant-out-of-range-compare)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# platform-specific stuff
|
||||||
|
if (WIN32)
|
||||||
|
add_definitions(-DBADVPN_USE_WINAPI -D_WIN32_WINNT=0x600 -DWIN32_LEAN_AND_MEAN)
|
||||||
|
add_definitions(-DBADVPN_THREAD_SAFE=0)
|
||||||
|
|
||||||
|
set(CMAKE_REQUIRED_DEFINITIONS "-D_WIN32_WINNT=0x600")
|
||||||
|
check_symbol_exists(WSAID_WSASENDMSG "winsock2.h;mswsock.h" HAVE_MSW_1)
|
||||||
|
check_symbol_exists(WSAID_WSARECVMSG "winsock2.h;mswsock.h" HAVE_MSW_2)
|
||||||
|
check_symbol_exists(WSAID_ACCEPTEX "winsock2.h;mswsock.h" HAVE_MSW_3)
|
||||||
|
check_symbol_exists(WSAID_GETACCEPTEXSOCKADDRS "winsock2.h;mswsock.h" HAVE_MSW_4)
|
||||||
|
check_symbol_exists(WSAID_CONNECTEX "winsock2.h;mswsock.h" HAVE_MSW_5)
|
||||||
|
set(CMAKE_REQUIRED_DEFINITIONS "")
|
||||||
|
if (NOT (HAVE_MSW_1 AND HAVE_MSW_2 AND HAVE_MSW_3 AND HAVE_MSW_4 AND HAVE_MSW_5))
|
||||||
|
add_definitions(-DBADVPN_USE_SHIPPED_MSWSOCK)
|
||||||
|
check_type_size(WSAMSG HAVE_WSAMSG)
|
||||||
|
if (NOT HAVE_WSAMSG)
|
||||||
|
add_definitions(-DBADVPN_SHIPPED_MSWSOCK_DECLARE_WSAMSG)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
else ()
|
||||||
|
set(BADVPN_THREADWORK_USE_PTHREAD 1)
|
||||||
|
add_definitions(-DBADVPN_THREADWORK_USE_PTHREAD)
|
||||||
|
add_definitions(-DBADVPN_THREAD_SAFE=1)
|
||||||
|
|
||||||
|
link_libraries(rt)
|
||||||
|
|
||||||
|
if (EMSCRIPTEN)
|
||||||
|
add_definitions(-DBADVPN_EMSCRIPTEN)
|
||||||
|
add_definitions(-DBADVPN_NO_PROCESS -DBADVPN_NO_UDEV -DBADVPN_NO_RANDOM)
|
||||||
|
elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
|
add_definitions(-DBADVPN_LINUX)
|
||||||
|
|
||||||
|
check_include_files(sys/signalfd.h HAVE_SYS_SIGNALFD_H)
|
||||||
|
if (HAVE_SYS_SIGNALFD_H)
|
||||||
|
add_definitions(-DBADVPN_USE_SIGNALFD)
|
||||||
|
else ()
|
||||||
|
add_definitions(-DBADVPN_USE_SELFPIPE)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
check_include_files(sys/epoll.h HAVE_SYS_EPOLL_H)
|
||||||
|
if (HAVE_SYS_EPOLL_H)
|
||||||
|
add_definitions(-DBADVPN_USE_EPOLL)
|
||||||
|
else ()
|
||||||
|
add_definitions(-DBADVPN_USE_POLL)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
check_include_files(linux/rfkill.h HAVE_LINUX_RFKILL_H)
|
||||||
|
if (HAVE_LINUX_RFKILL_H)
|
||||||
|
add_definitions(-DBADVPN_USE_LINUX_RFKILL)
|
||||||
|
set(BADVPN_USE_LINUX_RFKILL 1)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
check_include_files(linux/input.h HAVE_LINUX_INPUT_H)
|
||||||
|
if (HAVE_LINUX_INPUT_H)
|
||||||
|
add_definitions(-DBADVPN_USE_LINUX_INPUT)
|
||||||
|
set(BADVPN_USE_LINUX_INPUT 1)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
check_include_files(sys/inotify.h HAVE_SYS_INOTIFY_H)
|
||||||
|
if (HAVE_SYS_INOTIFY_H)
|
||||||
|
add_definitions(-DBADVPN_USE_INOTIFY)
|
||||||
|
set(BADVPN_USE_INOTIFY 1)
|
||||||
|
endif ()
|
||||||
|
elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||||
|
add_definitions(-DBADVPN_FREEBSD)
|
||||||
|
|
||||||
|
check_symbol_exists(kqueue "sys/types.h;sys/event.h;sys/time.h" HAVE_KQUEUE)
|
||||||
|
if (NOT HAVE_KQUEUE)
|
||||||
|
message(FATAL_ERROR "kqueue is required")
|
||||||
|
endif ()
|
||||||
|
add_definitions(-DBADVPN_USE_KEVENT)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (NOT DEFINED BADVPN_WITHOUT_CRYPTODEV)
|
||||||
|
check_include_files(crypto/cryptodev.h HAVE_CRYPTO_CRYPTODEV_H)
|
||||||
|
if (HAVE_CRYPTO_CRYPTODEV_H)
|
||||||
|
add_definitions(-DBADVPN_USE_CRYPTODEV)
|
||||||
|
elseif (DEFINED BADVPN_WITH_CRYPTODEV)
|
||||||
|
message(FATAL_ERROR "crypto/cryptodev.h not found")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# check for syslog
|
||||||
|
check_include_files(syslog.h HAVE_SYSLOG_H)
|
||||||
|
if (HAVE_SYSLOG_H)
|
||||||
|
add_definitions(-DBADVPN_USE_SYSLOG)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# add preprocessor definitions
|
||||||
|
if (BIG_ENDIAN)
|
||||||
|
add_definitions(-DBADVPN_BIG_ENDIAN)
|
||||||
|
else ()
|
||||||
|
add_definitions(-DBADVPN_LITTLE_ENDIAN)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# install man pages
|
||||||
|
install(
|
||||||
|
FILES badvpn.7
|
||||||
|
DESTINATION share/man/man7
|
||||||
|
)
|
||||||
|
|
||||||
|
# reset variables indicating whether we're building various libraries,
|
||||||
|
# and set them in the respective CMakeLists files. This is used to disable
|
||||||
|
# building examples and tests which require libraries that are not available.
|
||||||
|
set(BUILDING_SECURITY 0)
|
||||||
|
set(BUILDING_DHCPCLIENT 0)
|
||||||
|
set(BUILDING_ARPPROBE 0)
|
||||||
|
set(BUILDING_BKIO 0)
|
||||||
|
set(BUILDING_PREDICATE 0)
|
||||||
|
set(BUILDING_UDEVMONITOR 0)
|
||||||
|
set(BUILDING_THREADWORK 0)
|
||||||
|
set(BUILDING_RANDOM 0)
|
||||||
|
|
||||||
|
# Used to register an internal library.
|
||||||
|
# This will also add a library with the -plugin suffix, which is useful
|
||||||
|
# for use by dynamic libraries (e.g. NCD modules):
|
||||||
|
# - If BUILD_SHARED_LIBS is off (default), the libraries ${LIB_NAME} and ${LIB_NAME}-plugin
|
||||||
|
# are built separately. Both are static libraries but the -plugin variant is build as position
|
||||||
|
# independent code, so it can be (statically) linked into dynamic libraries.
|
||||||
|
# - If BUILD_SHARED_LIBS is on, only ${LIB_NAME} is built, as a shared library.
|
||||||
|
# The ${LIB_NAME}-plugin target is set up as an alias to ${LIB_NAME}.
|
||||||
|
function(badvpn_add_library LIB_NAME LINK_BADVPN_LIBS LINK_SYS_LIBS LIB_SOURCES)
|
||||||
|
set(BADVPN_LIBS_EXEC)
|
||||||
|
set(BADVPN_LIBS_PLUGIN)
|
||||||
|
foreach(LIB ${LINK_BADVPN_LIBS})
|
||||||
|
list(APPEND BADVPN_LIBS_EXEC "${LIB}")
|
||||||
|
list(APPEND BADVPN_LIBS_PLUGIN "${LIB}-plugin")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
add_library("${LIB_NAME}" ${LIB_SOURCES})
|
||||||
|
target_link_libraries("${LIB_NAME}" ${BADVPN_LIBS_EXEC} ${LINK_SYS_LIBS})
|
||||||
|
set_target_properties("${LIB_NAME}" PROPERTIES OUTPUT_NAME "badvpn-${LIB_NAME}")
|
||||||
|
|
||||||
|
if (BUILD_SHARED_LIBS)
|
||||||
|
add_library("${LIB_NAME}-plugin" ALIAS "${LIB_NAME}")
|
||||||
|
else ()
|
||||||
|
add_library("${LIB_NAME}-plugin" STATIC ${LIB_SOURCES})
|
||||||
|
target_link_libraries("${LIB_NAME}-plugin" ${BADVPN_LIBS_PLUGIN} ${LINK_SYS_LIBS})
|
||||||
|
set_target_properties("${LIB_NAME}-plugin" PROPERTIES OUTPUT_NAME "badvpn-${LIB_NAME}-plugin")
|
||||||
|
set_target_properties("${LIB_NAME}-plugin" PROPERTIES POSITION_INDEPENDENT_CODE YES)
|
||||||
|
set_target_properties("${LIB_NAME}-plugin" PROPERTIES COMPILE_FLAGS "-fvisibility=hidden -DBADVPN_PLUGIN")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# internal libraries
|
||||||
|
add_subdirectory(base)
|
||||||
|
add_subdirectory(system)
|
||||||
|
add_subdirectory(flow)
|
||||||
|
add_subdirectory(flowextra)
|
||||||
|
if (OpenSSL_FOUND)
|
||||||
|
set(BUILDING_SECURITY 1)
|
||||||
|
add_subdirectory(security)
|
||||||
|
endif ()
|
||||||
|
if (NSS_FOUND)
|
||||||
|
add_subdirectory(nspr_support)
|
||||||
|
endif ()
|
||||||
|
if (BUILD_CLIENT OR BUILDING_SECURITY)
|
||||||
|
set(BUILDING_THREADWORK 1)
|
||||||
|
add_subdirectory(threadwork)
|
||||||
|
endif ()
|
||||||
|
if (BUILD_CLIENT OR BUILD_TUN2SOCKS)
|
||||||
|
add_subdirectory(tuntap)
|
||||||
|
endif ()
|
||||||
|
if (BUILD_SERVER)
|
||||||
|
set(BUILDING_PREDICATE 1)
|
||||||
|
add_subdirectory(predicate)
|
||||||
|
endif ()
|
||||||
|
if (BUILD_CLIENT OR BUILD_FLOODER)
|
||||||
|
add_subdirectory(server_connection)
|
||||||
|
endif ()
|
||||||
|
if (BUILD_NCD AND NOT EMSCRIPTEN)
|
||||||
|
set(BUILDING_DHCPCLIENT 1)
|
||||||
|
set(BUILDING_ARPPROBE 1)
|
||||||
|
set(BUILDING_UDEVMONITOR 1)
|
||||||
|
set(BUILDING_RANDOM 1)
|
||||||
|
add_subdirectory(stringmap)
|
||||||
|
add_subdirectory(udevmonitor)
|
||||||
|
add_subdirectory(dhcpclient)
|
||||||
|
add_subdirectory(arpprobe)
|
||||||
|
add_subdirectory(random)
|
||||||
|
endif ()
|
||||||
|
if (BUILD_TUN2SOCKS)
|
||||||
|
add_subdirectory(socksclient)
|
||||||
|
add_subdirectory(udpgw_client)
|
||||||
|
add_subdirectory(lwip)
|
||||||
|
endif ()
|
||||||
|
if (BUILD_TUNCTL)
|
||||||
|
add_subdirectory(tunctl)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# example programs
|
||||||
|
if (BUILD_EXAMPLES)
|
||||||
|
add_subdirectory(examples)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# tests
|
||||||
|
if (BUILD_TESTS)
|
||||||
|
add_subdirectory(tests)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# server
|
||||||
|
if (BUILD_SERVER)
|
||||||
|
add_subdirectory(server)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# client
|
||||||
|
if (BUILD_CLIENT)
|
||||||
|
add_subdirectory(client)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# flooder
|
||||||
|
if (BUILD_FLOODER)
|
||||||
|
add_subdirectory(flooder)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# tun2socks
|
||||||
|
if (BUILD_TUN2SOCKS)
|
||||||
|
add_subdirectory(tun2socks)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# udpgw
|
||||||
|
if (BUILD_UDPGW)
|
||||||
|
add_subdirectory(udpgw)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# ncd
|
||||||
|
if (BUILD_NCD)
|
||||||
|
add_subdirectory(ncd)
|
||||||
|
if (NOT EMSCRIPTEN)
|
||||||
|
add_subdirectory(ncd-request)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# dostest
|
||||||
|
if (BUILD_DOSTEST)
|
||||||
|
add_subdirectory(dostest)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
message(STATUS "Building components:")
|
||||||
|
|
||||||
|
# print what we're building and what not
|
||||||
|
foreach (name ${BUILD_COMPONENTS})
|
||||||
|
# to lower name
|
||||||
|
string(TOLOWER "${name}" name_withspaces)
|
||||||
|
|
||||||
|
# append spaces to name
|
||||||
|
#while (TRUE)
|
||||||
|
# string(LENGTH "${name_withspaces}" length)
|
||||||
|
# if (NOT (length LESS 12))
|
||||||
|
# break()
|
||||||
|
# endif ()
|
||||||
|
# set(name_withspaces "${name_withspaces} ")
|
||||||
|
#endwhile ()
|
||||||
|
|
||||||
|
# determine if we're building
|
||||||
|
if (BUILD_${name})
|
||||||
|
set(building "yes")
|
||||||
|
else ()
|
||||||
|
set(building "no")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
message(STATUS " ${name_withspaces} ${building}")
|
||||||
|
endforeach ()
|
|
@ -0,0 +1,24 @@
|
||||||
|
Copyright (c) 2009, Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the author nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,216 @@
|
||||||
|
Version 1.999.129:
|
||||||
|
|
||||||
|
- ncd: modules: file_open: Fix typo in assertion.
|
||||||
|
|
||||||
|
- server: Fix bug forgetting to call BSSLConnection_ReleaseBuffers(). Unless threads are enabled, this is an assert failure if NDEBUG is not defined an a non-issue otherwise.
|
||||||
|
|
||||||
|
- ncd: Look for various programs in PATH instead of hardcoded paths.
|
||||||
|
|
||||||
|
- Add compile-udpgw.sh.
|
||||||
|
|
||||||
|
- ncd: modules: net_dns: Implement net.dns.resolvconf() forspecification of arbitrary resolv.conf lines
|
||||||
|
|
||||||
|
Version 1.999.128:
|
||||||
|
|
||||||
|
- tun2socks: add option --append-source-to-username to give the SOCKS server the source IP of the connection
|
||||||
|
|
||||||
|
- tun2socks: IPv6 support, and updated to newer version of lwIP
|
||||||
|
|
||||||
|
- tun2socks: fix some bugs/crashes
|
||||||
|
|
||||||
|
- tun2socks, udpgw: transparent DNS forwarding, though no Windows support on udpgw side (contributed by Kerem Hadimli)
|
||||||
|
|
||||||
|
- NCD: preliminary support for dynamically loading commands
|
||||||
|
|
||||||
|
Version 1.999.127:
|
||||||
|
|
||||||
|
- client, server: implement experimental support for performing SSL operations in worker threads. Currently it's rather inefficient.
|
||||||
|
|
||||||
|
- NCD: modules: value: implement value::append() for appending to a list
|
||||||
|
|
||||||
|
- NCD: modules: net_iptables: add single-argument form of append and insert commands, allowing for generic use
|
||||||
|
|
||||||
|
- NCD: modules: net_iptables: implement net.iptables.insert() and net.ebtables.insert()
|
||||||
|
|
||||||
|
- NCD: modules: sys_start_process: implement options, including username, term_on_deinit and deinit_kill_time
|
||||||
|
|
||||||
|
- NCD: modules: sys_request_server: implement _caller in request handler
|
||||||
|
|
||||||
|
- NCD: modules: add getenv()
|
||||||
|
|
||||||
|
- NCD: modules: daemon: implement options, including username option
|
||||||
|
|
||||||
|
- NCD: modules: runonce: add new options format with a map, implement username option
|
||||||
|
|
||||||
|
- NCD: modules: add buffer(), which exposes a buffer with efficient appending and removing from the beginning.
|
||||||
|
|
||||||
|
- NCD: add a new internal string representation called ComposedString. This allows modules to expose the concatenation of multiple memroy buffers as a single string value, efficiently.
|
||||||
|
|
||||||
|
- fix many, hopefully all, strict aliasing violations. In particular, this fixes a bug where the DHCP client integrated into NCD won't work correctly, subject to optimization flags.
|
||||||
|
|
||||||
|
- NCD: modules: sleep: interpret empty string as no sleeping, add sleep(ms_start) with one argument
|
||||||
|
|
||||||
|
- NCD: modules: add log(), log_r() and log_fr() commands for logging via the BLog system
|
||||||
|
|
||||||
|
Version 1.999.126:
|
||||||
|
|
||||||
|
- NCD: modules: sleep: interpret empty string time as no sleeping, add sleep(ms_start) with one argument
|
||||||
|
|
||||||
|
- NCD: modules: add log module for message logging using the BLog system
|
||||||
|
|
||||||
|
- NCD: implement the "include" and "include_guard" directives, which allow separating reusable code into files
|
||||||
|
|
||||||
|
- NCD: modules: call2: implement call_with_caller_target(), which makes it easier to write reusable code that calls back user-provided code
|
||||||
|
|
||||||
|
- NCD: modules: call2: remove call2_if(), call2_ifelse(), embcall2(), embcall2_if(), embcall2_ifelse()
|
||||||
|
|
||||||
|
- NCD: modules: add sys.start_process(), which implements starting and controlling external processes and reading/writing their stdout/stdin
|
||||||
|
|
||||||
|
- tun2socks: implement SOCKS password authentication
|
||||||
|
|
||||||
|
- NCD: track the depth of values and limit the maximum depth. This avoids stack overflow with very deeply nested values.
|
||||||
|
|
||||||
|
- NCD: modules: add substr()
|
||||||
|
|
||||||
|
- NCD: process_manager: add 2-argument start() method which doesn't take a process identifier
|
||||||
|
|
||||||
|
- NCD: process_manager: allow process identifiers to be any value not just strings
|
||||||
|
|
||||||
|
- NCD: multidepend, depend_scope: fix immediate effect order when a depend finishes backtracking
|
||||||
|
|
||||||
|
- NCD: add depend_scope module to do exactly what the multidepend module does, but with separate non-global dependency name scopes
|
||||||
|
|
||||||
|
- NCD: multidepend: allow dependency names to be any value not just strings
|
||||||
|
|
||||||
|
- NCD: implement value::insert(what) for appending to a list
|
||||||
|
|
||||||
|
- NCD: change the format of addresses in sys.request_server() and sys.request_client() to be the same as in the socket module
|
||||||
|
|
||||||
|
- NCD: add socket module (sys.connect() and sys.listen())
|
||||||
|
|
||||||
|
- NCD: fix bug where duplicate template/process names would not be detected and weird behaviour would result
|
||||||
|
|
||||||
|
- NCD: add backtrack_point() for simple backtracking
|
||||||
|
|
||||||
|
- NCD: add file_open() for more complete file I/O
|
||||||
|
|
||||||
|
- NCD: implement parse_ipv6_addr() and parse_ipv6_cidr_addr()
|
||||||
|
|
||||||
|
- NCD: port to Emscripten/Javascript, for the in-browser demo
|
||||||
|
|
||||||
|
- NCD: many performance and memory usage improvements
|
||||||
|
|
||||||
|
- NCD: add assert_false()
|
||||||
|
|
||||||
|
- NCD: don't link to OpenSSL to for random number generator. Use /dev/urandom instead to generate XIDs for DHCP.
|
||||||
|
|
||||||
|
- NCD: deprecate ip_in_network() and instead add net.ipv{4,6}.addr_in_network(), net.ipv{4,6}.ifnot_addr_in_network()
|
||||||
|
|
||||||
|
- NCD: implement some IPv6 modules: net.ipv6.addr(), net.ipv6.route()
|
||||||
|
|
||||||
|
- NCD: support CIDR style addr/prefix addresses in various modules
|
||||||
|
|
||||||
|
- NCD: recognize Elif and Else with capital first letter to be consistent with other reserved keywords
|
||||||
|
|
||||||
|
Version 1.999.123:
|
||||||
|
|
||||||
|
- NCD: performance improvements related to finding modules for statements
|
||||||
|
|
||||||
|
- NCD: performance improvements related to resolving object names
|
||||||
|
|
||||||
|
- NCD: performance improvements related to instantiating statement arguments
|
||||||
|
|
||||||
|
- NCD: add value::replace_this() and value::replace_this_undo()
|
||||||
|
|
||||||
|
- NCD: add value::reset()
|
||||||
|
|
||||||
|
- NCD: add value::replace() and value::replace_undo()
|
||||||
|
|
||||||
|
- Port to compile with MSVC for Windows.
|
||||||
|
|
||||||
|
- NCD: add Foreach clause
|
||||||
|
|
||||||
|
- NCD: implement _caller in spawn(), add spawn::join()
|
||||||
|
|
||||||
|
- NCD: add explode()
|
||||||
|
|
||||||
|
- NCD: add hard_reboot() and hard_poweroff()
|
||||||
|
|
||||||
|
- NCD: add file_stat() and file_lstat()
|
||||||
|
|
||||||
|
- NCD: fix regex_replace() semantics. It was very broken because it did a complete replacement pass for every regex on the list, so it would match parts that have already been replaced, producing unexpected results.
|
||||||
|
|
||||||
|
- NCD: small performance improvement
|
||||||
|
|
||||||
|
Version 1.999.121:
|
||||||
|
|
||||||
|
- NCD: improve error handling semantics; see http://code.google.com/p/badvpn/source/detail?r=1376
|
||||||
|
|
||||||
|
- NCD: fix assertion failure in sys.evdev() if a device error occurs (e.g. device unplugged) while an event is being processed. Similar fix in some other modules, but these may not be reproducable.
|
||||||
|
|
||||||
|
- NCD: some more performance improvements
|
||||||
|
|
||||||
|
- NCD: some performance improvements (~30% faster interpretation of cpu-bound code)
|
||||||
|
|
||||||
|
- NCD: implemented If..elif..else clause.
|
||||||
|
|
||||||
|
- NCD: net.backend.wpa_supplicant: fix to work with wpa_supplicant>=1.0
|
||||||
|
|
||||||
|
Version 1.999.115:
|
||||||
|
|
||||||
|
- NCD: Many improvements; new statements, including call(), alias(), foreach(), choose().
|
||||||
|
|
||||||
|
Version 1.999.113:
|
||||||
|
|
||||||
|
- NCD: when starting child processes, make sure that file descriptors for standard
|
||||||
|
streams are always open in the child, by opening /dev/null if they are not.
|
||||||
|
|
||||||
|
- Improve build system to allow selective building of components.
|
||||||
|
By default, everything is built, unless -DBUILD_NOTHING_BY_DEFAULT=1 is given.
|
||||||
|
Individual components can then be enabled or disabled using -DBUILD_COMPONENT=1
|
||||||
|
and -DBUILD_COMPONENT=0.
|
||||||
|
|
||||||
|
- When starting any BadVPN program, make sure that file descriptors for standard
|
||||||
|
streams are always open in the child, by opening /dev/null if they are not.
|
||||||
|
|
||||||
|
- NCD: net.backend.wpa_supplicant(): add 'bssid' and 'ssid' variables to allow
|
||||||
|
determining what wireless network wpa_supplicant connected to.
|
||||||
|
|
||||||
|
- NCD: net.backend.wpa_supplicant(): do not require the user to start wpa_supplicant via
|
||||||
|
stdbuf, but do it automatically.
|
||||||
|
|
||||||
|
Version 1.999.111:
|
||||||
|
|
||||||
|
- Improved protocol such that peers can use SSL when comminicating via the server. This
|
||||||
|
improves security, as compromising the server will not allow the attacker to see secret
|
||||||
|
data shared by peers (in particular, encryption keys and OTP seeds when in UDP mode).
|
||||||
|
|
||||||
|
Compatibility is preserved if an only if the following conditions are met:
|
||||||
|
- The server is using the latest version.
|
||||||
|
- If the network is using SSL, all clients using the new version are using the
|
||||||
|
"--allow-peer-talk-without-ssl" command line option.
|
||||||
|
|
||||||
|
Be aware, however, that using the "--allow-peer-talk-without-ssl" option negates the
|
||||||
|
security benefits of the new SSL support - not only between pairs of peers where one
|
||||||
|
peer is using the old version, but also between pairs where both peers are capable
|
||||||
|
of SSL. This is because the server can re-initialize the pair, telling them not to use
|
||||||
|
SSL.
|
||||||
|
|
||||||
|
Version 1.999.107:
|
||||||
|
|
||||||
|
- Added Windows IOCP support, removing the limitation on ~64 connections. This is important
|
||||||
|
for tun2socks, which may have to handle several hundred connections.
|
||||||
|
|
||||||
|
Version 1.999.105.2:
|
||||||
|
|
||||||
|
- Fixed an assertion failure in tun2socks related to sending data to SOCKS.
|
||||||
|
|
||||||
|
Version 1.999.101.3:
|
||||||
|
|
||||||
|
- Fixed UDP transport on Windows 7 which didn't work (was only tested on XP).
|
||||||
|
|
||||||
|
Version 1.999.101:
|
||||||
|
|
||||||
|
- Fixed a protocol issue present in versions <=1.999.100.3. Compatibility is preserved in
|
||||||
|
case of a new server and old clients, but it is not possible to connect to an old server
|
||||||
|
with a new client.
|
|
@ -0,0 +1,76 @@
|
||||||
|
1 Requirements
|
||||||
|
|
||||||
|
1.1 Operating system
|
||||||
|
|
||||||
|
Linux:
|
||||||
|
- Linux kernel 2.6. Kernel 2.4 will work, but performance will suffer.
|
||||||
|
- tested on x86, x86_64 and ARM architectures. Not tested on any big-endian architecture.
|
||||||
|
|
||||||
|
Windows:
|
||||||
|
- Windows XP or newer; tested on Windows XP and Windows 7
|
||||||
|
|
||||||
|
FreeBSD:
|
||||||
|
- Not regularly tested.
|
||||||
|
|
||||||
|
Other systems are not supported.
|
||||||
|
|
||||||
|
1.2 Compilers
|
||||||
|
|
||||||
|
Linux:
|
||||||
|
- gcc
|
||||||
|
- clang, except >=3.0 (clang bug http://llvm.org/bugs/show_bug.cgi?id=11535)
|
||||||
|
|
||||||
|
Windows:
|
||||||
|
- gcc from the mingw-w64 project for 32-bit targets
|
||||||
|
|
||||||
|
C language features used:
|
||||||
|
- Standard (all part of C99):
|
||||||
|
- designated initializers
|
||||||
|
- stdint.h, inttypes.h, stddef.h
|
||||||
|
- intermingled declarations and code
|
||||||
|
- for loop initial declaration
|
||||||
|
- one-line "//" comments
|
||||||
|
- Extensions:
|
||||||
|
- packed structure attribute (to pack a structure and allow unaligned access)
|
||||||
|
|
||||||
|
1.3 CMake
|
||||||
|
|
||||||
|
The build system uses CMake.
|
||||||
|
|
||||||
|
1.4 OpenSSL
|
||||||
|
|
||||||
|
Libcrypto (part of OpenSSL) is used for block ciphers, hash functions and random data generation.
|
||||||
|
|
||||||
|
1.5 Network Security Services (NSS)
|
||||||
|
|
||||||
|
The NSS library from Mozilla is used for TLS support. NSS command-line tools are also needed
|
||||||
|
for setting up certificates.
|
||||||
|
|
||||||
|
1.6 TAP-Win32 (Windows only) (runtime only)
|
||||||
|
|
||||||
|
The TAP-Win32 driver, part of OpenVPN.
|
||||||
|
|
||||||
|
2 Compilation
|
||||||
|
|
||||||
|
2.1 Compiling on Linux
|
||||||
|
|
||||||
|
$ tar xf badvpn-<version>.tar.bz2
|
||||||
|
$ mkdir build
|
||||||
|
$ cd build
|
||||||
|
$ cmake ../badvpn-<version> -DCMAKE_INSTALL_PREFIX=/usr/local
|
||||||
|
$ make
|
||||||
|
If you want to install it, run as root:
|
||||||
|
# make install
|
||||||
|
|
||||||
|
If you only want NCD or tun2socks and not the VPN system, you can avoid the NSS dependency by passing
|
||||||
|
the following to the cmake command:
|
||||||
|
-DBUILD_NCD=1 -DBUILD_TUN2SOCKS=1 -DBUILD_NOTHING_BY_DEFAULT=1
|
||||||
|
|
||||||
|
2.2 Compiling for Windows
|
||||||
|
|
||||||
|
See the file INSTALL-WINDOWS for detailed instructions.
|
||||||
|
|
||||||
|
3 Usage
|
||||||
|
|
||||||
|
The primary documentation is on the BadVPN homepage, http://code.google.com/p/badvpn/ .
|
||||||
|
Additionally, some man pages are installed (badvpn(7), badvpn-server(8), badvpn-client(8)).
|
|
@ -0,0 +1,72 @@
|
||||||
|
There are many ways to build BadVPN for Windows. It can be built with MSVC or GCC compilers,
|
||||||
|
and it be built natively from Windows or cross-compiled from Linux. However, this document
|
||||||
|
only describes building natively from Windows using MSVC.
|
||||||
|
|
||||||
|
1. Get a MSVC compiler, e.g. from Visual Studio, Visual Studio Express or from the Windows SDK.
|
||||||
|
|
||||||
|
2. Choose a directory where built stuff will be installed into; we call it <root>.
|
||||||
|
|
||||||
|
3. Build the NSS library.
|
||||||
|
NOTE: you can also use the prebuilt version in the BadVPN windows download.
|
||||||
|
|
||||||
|
- Install MozillaBuild:
|
||||||
|
http://ftp.mozilla.org/pub/mozilla.org/mozilla/libraries/win32/MozillaBuildSetup-Latest.exe .
|
||||||
|
|
||||||
|
- Download the NSS source code that includes NSPR. As of the time of writing the latest version was 3.13.5:
|
||||||
|
https://ftp.mozilla.org/pub/mozilla.org/security/nss/releases/NSS_3_13_5_RTM/src/nss-3.13.5-with-nspr-4.9.1.tar.gz .
|
||||||
|
|
||||||
|
Extract it to c:\ so that you have C:\mozilla .
|
||||||
|
|
||||||
|
- Open a terminal with access to the Visual Studio compilers and other tools. E.g. if you use the Windows SDK,
|
||||||
|
activate the following start menu item: Programs -> Microsoft Windows SDK v7.1 -> Windows SDK 7.1 Command Prompt.
|
||||||
|
|
||||||
|
- In this terminal, run:
|
||||||
|
|
||||||
|
> c:\mozilla-build\start-l10n.bat
|
||||||
|
|
||||||
|
- Either a new terminal opens with a bash shell, or a bash shell starts in the existing terminal. Either way,
|
||||||
|
enter the following commands to finally build NSS: (here paths are written as /driveletter/...)
|
||||||
|
|
||||||
|
$ export OS_TARGET=WINNT
|
||||||
|
$ export BUILD_OPT=1
|
||||||
|
$ cd <nss_source_dir>/mozilla/security/nss
|
||||||
|
$ make nss_build_all
|
||||||
|
|
||||||
|
Now use a script shipped with the BadVPN source to copy the resulting files into appropriate directories within <root>:
|
||||||
|
|
||||||
|
$ <badvpn_source_dir>/scripts/copy_nss ../../dist <root>
|
||||||
|
|
||||||
|
4. Build the OpenSSL library.
|
||||||
|
NOTE: you can also use the prebuilt version in the BadVPN windows download.
|
||||||
|
|
||||||
|
- Install ActivePerl.
|
||||||
|
|
||||||
|
- Download the OpenSSL source code and extract it.
|
||||||
|
|
||||||
|
- Open a compiler terminal, as was done when building NSS. Inside it, run:
|
||||||
|
|
||||||
|
> cd <openssl_source_dir>
|
||||||
|
> perl Configure VC-WIN32 --prefix=<root>
|
||||||
|
> ms\do_ms
|
||||||
|
> nmake -f ms\ntdll.mak
|
||||||
|
|
||||||
|
To copy the results into <root>:
|
||||||
|
|
||||||
|
> nmake -f ms\ntdll.mak install
|
||||||
|
|
||||||
|
5. Build BadVPN.
|
||||||
|
|
||||||
|
- Install CMake. During installation, select the option to include cmake in PATH
|
||||||
|
to avoid having to type a long path into the terminal.
|
||||||
|
|
||||||
|
- Create an empty folder where BadVPN will be built; call it <build>.
|
||||||
|
|
||||||
|
- Open a compiler terminal. Inside it, run:
|
||||||
|
|
||||||
|
> cd <build>
|
||||||
|
> cmake <badvpn_source_dir> -G "NMake Makefiles" -DCMAKE_INSTALL_PREFIX=<root> -DCMAKE_BUILD_TYPE=Release
|
||||||
|
> nmake
|
||||||
|
|
||||||
|
To copy the results into <root>:
|
||||||
|
|
||||||
|
> nmake install
|
|
@ -0,0 +1,359 @@
|
||||||
|
/**
|
||||||
|
* @file BArpProbe.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <net/if_arp.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <linux/filter.h>
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <misc/byteorder.h>
|
||||||
|
#include <misc/ethernet_proto.h>
|
||||||
|
#include <misc/ipv4_proto.h>
|
||||||
|
#include <misc/udp_proto.h>
|
||||||
|
#include <misc/get_iface_info.h>
|
||||||
|
#include <base/BLog.h>
|
||||||
|
|
||||||
|
#include "BArpProbe.h"
|
||||||
|
|
||||||
|
#include <generated/blog_channel_BArpProbe.h>
|
||||||
|
|
||||||
|
#define STATE_INITIAL 1
|
||||||
|
#define STATE_NOEXIST 2
|
||||||
|
#define STATE_EXIST 3
|
||||||
|
#define STATE_EXIST_PANIC 4
|
||||||
|
|
||||||
|
static void dgram_handler (BArpProbe *o, int event)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
BLog(BLOG_ERROR, "packet socket error");
|
||||||
|
|
||||||
|
// report error
|
||||||
|
DEBUGERROR(&o->d_err, o->handler(o->user, BARPPROBE_EVENT_ERROR));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_request (BArpProbe *o)
|
||||||
|
{
|
||||||
|
if (o->send_sending) {
|
||||||
|
BLog(BLOG_ERROR, "cannot send packet while another packet is being sent!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// build packet
|
||||||
|
struct arp_packet *arp = &o->send_packet;
|
||||||
|
arp->hardware_type = hton16(ARP_HARDWARE_TYPE_ETHERNET);
|
||||||
|
arp->protocol_type = hton16(ETHERTYPE_IPV4);
|
||||||
|
arp->hardware_size = hton8(6);
|
||||||
|
arp->protocol_size = hton8(4);
|
||||||
|
arp->opcode = hton16(ARP_OPCODE_REQUEST);
|
||||||
|
memcpy(arp->sender_mac, o->if_mac, 6);
|
||||||
|
arp->sender_ip = hton32(0);
|
||||||
|
memset(arp->target_mac, 0, sizeof(arp->target_mac));
|
||||||
|
arp->target_ip = o->addr;
|
||||||
|
|
||||||
|
// send packet
|
||||||
|
PacketPassInterface_Sender_Send(o->send_if, (uint8_t *)&o->send_packet, sizeof(o->send_packet));
|
||||||
|
|
||||||
|
// set sending
|
||||||
|
o->send_sending = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_if_handler_done (BArpProbe *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(o->send_sending)
|
||||||
|
|
||||||
|
// set not sending
|
||||||
|
o->send_sending = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void recv_if_handler_done (BArpProbe *o, int data_len)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(data_len >= 0)
|
||||||
|
ASSERT(data_len <= sizeof(struct arp_packet))
|
||||||
|
|
||||||
|
// receive next packet
|
||||||
|
PacketRecvInterface_Receiver_Recv(o->recv_if, (uint8_t *)&o->recv_packet);
|
||||||
|
|
||||||
|
if (data_len != sizeof(struct arp_packet)) {
|
||||||
|
BLog(BLOG_WARNING, "receive: wrong size");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct arp_packet *arp = &o->recv_packet;
|
||||||
|
|
||||||
|
if (ntoh16(arp->hardware_type) != ARP_HARDWARE_TYPE_ETHERNET) {
|
||||||
|
BLog(BLOG_WARNING, "receive: wrong hardware type");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ntoh16(arp->protocol_type) != ETHERTYPE_IPV4) {
|
||||||
|
BLog(BLOG_WARNING, "receive: wrong protocol type");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ntoh8(arp->hardware_size) != 6) {
|
||||||
|
BLog(BLOG_WARNING, "receive: wrong hardware size");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ntoh8(arp->protocol_size) != 4) {
|
||||||
|
BLog(BLOG_WARNING, "receive: wrong protocol size");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ntoh16(arp->opcode) != ARP_OPCODE_REPLY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arp->sender_ip != o->addr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int old_state = o->state;
|
||||||
|
|
||||||
|
// set minus one missed
|
||||||
|
o->num_missed = -1;
|
||||||
|
|
||||||
|
// set timer
|
||||||
|
BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_EXIST_WAITSEND);
|
||||||
|
|
||||||
|
// set state exist
|
||||||
|
o->state = STATE_EXIST;
|
||||||
|
|
||||||
|
// report exist if needed
|
||||||
|
if (old_state == STATE_INITIAL || old_state == STATE_NOEXIST) {
|
||||||
|
o->handler(o->user, BARPPROBE_EVENT_EXIST);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void timer_handler (BArpProbe *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// send request
|
||||||
|
send_request(o);
|
||||||
|
|
||||||
|
switch (o->state) {
|
||||||
|
case STATE_INITIAL: {
|
||||||
|
ASSERT(o->num_missed >= 0)
|
||||||
|
ASSERT(o->num_missed < BARPPROBE_INITIAL_NUM_ATTEMPTS)
|
||||||
|
|
||||||
|
// increment missed
|
||||||
|
o->num_missed++;
|
||||||
|
|
||||||
|
// all attempts failed?
|
||||||
|
if (o->num_missed == BARPPROBE_INITIAL_NUM_ATTEMPTS) {
|
||||||
|
// set timer
|
||||||
|
BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_NOEXIST_WAITRECV);
|
||||||
|
|
||||||
|
// set state noexist
|
||||||
|
o->state = STATE_NOEXIST;
|
||||||
|
|
||||||
|
// report noexist
|
||||||
|
o->handler(o->user, BARPPROBE_EVENT_NOEXIST);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set timer
|
||||||
|
BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_INITIAL_WAITRECV);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case STATE_NOEXIST: {
|
||||||
|
// set timer
|
||||||
|
BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_NOEXIST_WAITRECV);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case STATE_EXIST: {
|
||||||
|
ASSERT(o->num_missed >= -1)
|
||||||
|
ASSERT(o->num_missed < BARPPROBE_EXIST_NUM_NOREPLY)
|
||||||
|
|
||||||
|
// increment missed
|
||||||
|
o->num_missed++;
|
||||||
|
|
||||||
|
// all missed?
|
||||||
|
if (o->num_missed == BARPPROBE_EXIST_NUM_NOREPLY) {
|
||||||
|
// set timer
|
||||||
|
BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_EXIST_PANIC_WAITRECV);
|
||||||
|
|
||||||
|
// set zero missed
|
||||||
|
o->num_missed = 0;
|
||||||
|
|
||||||
|
// set state panic
|
||||||
|
o->state = STATE_EXIST_PANIC;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set timer
|
||||||
|
BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_EXIST_WAITRECV);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case STATE_EXIST_PANIC: {
|
||||||
|
ASSERT(o->num_missed >= 0)
|
||||||
|
ASSERT(o->num_missed < BARPPROBE_EXIST_PANIC_NUM_NOREPLY)
|
||||||
|
|
||||||
|
// increment missed
|
||||||
|
o->num_missed++;
|
||||||
|
|
||||||
|
// all missed?
|
||||||
|
if (o->num_missed == BARPPROBE_EXIST_PANIC_NUM_NOREPLY) {
|
||||||
|
// set timer
|
||||||
|
BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_NOEXIST_WAITRECV);
|
||||||
|
|
||||||
|
// set state panic
|
||||||
|
o->state = STATE_NOEXIST;
|
||||||
|
|
||||||
|
// report noexist
|
||||||
|
o->handler(o->user, BARPPROBE_EVENT_NOEXIST);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set timer
|
||||||
|
BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_EXIST_PANIC_WAITRECV);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int BArpProbe_Init (BArpProbe *o, const char *ifname, uint32_t addr, BReactor *reactor, void *user, BArpProbe_handler handler)
|
||||||
|
{
|
||||||
|
ASSERT(ifname)
|
||||||
|
ASSERT(handler)
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
o->addr = addr;
|
||||||
|
o->reactor = reactor;
|
||||||
|
o->user = user;
|
||||||
|
o->handler = handler;
|
||||||
|
|
||||||
|
// get interface information
|
||||||
|
int if_mtu;
|
||||||
|
int if_index;
|
||||||
|
if (!badvpn_get_iface_info(ifname, o->if_mac, &if_mtu, &if_index)) {
|
||||||
|
BLog(BLOG_ERROR, "failed to get interface information");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *if_mac = o->if_mac;
|
||||||
|
BLog(BLOG_INFO, "if_mac=%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8" if_mtu=%d if_index=%d",
|
||||||
|
if_mac[0], if_mac[1], if_mac[2], if_mac[3], if_mac[4], if_mac[5], if_mtu, if_index);
|
||||||
|
|
||||||
|
// check MTU
|
||||||
|
if (if_mtu < sizeof(struct arp_packet)) {
|
||||||
|
BLog(BLOG_ERROR, "MTU is too small for ARP !?!");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init dgram
|
||||||
|
if (!BDatagram_Init(&o->dgram, BADDR_TYPE_PACKET, o->reactor, o, (BDatagram_handler)dgram_handler)) {
|
||||||
|
BLog(BLOG_ERROR, "BDatagram_Init failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bind dgram
|
||||||
|
BAddr bind_addr;
|
||||||
|
BAddr_InitPacket(&bind_addr, hton16(ETHERTYPE_ARP), if_index, BADDR_PACKET_HEADER_TYPE_ETHERNET, BADDR_PACKET_PACKET_TYPE_HOST, if_mac);
|
||||||
|
if (!BDatagram_Bind(&o->dgram, bind_addr)) {
|
||||||
|
BLog(BLOG_ERROR, "BDatagram_Bind failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set dgram send addresses
|
||||||
|
BAddr dest_addr;
|
||||||
|
uint8_t broadcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
||||||
|
BAddr_InitPacket(&dest_addr, hton16(ETHERTYPE_ARP), if_index, BADDR_PACKET_HEADER_TYPE_ETHERNET, BADDR_PACKET_PACKET_TYPE_BROADCAST, broadcast_mac);
|
||||||
|
BIPAddr local_addr;
|
||||||
|
BIPAddr_InitInvalid(&local_addr);
|
||||||
|
BDatagram_SetSendAddrs(&o->dgram, dest_addr, local_addr);
|
||||||
|
|
||||||
|
// init send interface
|
||||||
|
BDatagram_SendAsync_Init(&o->dgram, sizeof(struct arp_packet));
|
||||||
|
o->send_if = BDatagram_SendAsync_GetIf(&o->dgram);
|
||||||
|
PacketPassInterface_Sender_Init(o->send_if, (PacketPassInterface_handler_done)send_if_handler_done, o);
|
||||||
|
|
||||||
|
// set not sending
|
||||||
|
o->send_sending = 0;
|
||||||
|
|
||||||
|
// init recv interface
|
||||||
|
BDatagram_RecvAsync_Init(&o->dgram, sizeof(struct arp_packet));
|
||||||
|
o->recv_if = BDatagram_RecvAsync_GetIf(&o->dgram);
|
||||||
|
PacketRecvInterface_Receiver_Init(o->recv_if, (PacketRecvInterface_handler_done)recv_if_handler_done, o);
|
||||||
|
|
||||||
|
// init timer
|
||||||
|
BTimer_Init(&o->timer, 0, (BTimer_handler)timer_handler, o);
|
||||||
|
|
||||||
|
// receive first packet
|
||||||
|
PacketRecvInterface_Receiver_Recv(o->recv_if, (uint8_t *)&o->recv_packet);
|
||||||
|
|
||||||
|
// send request
|
||||||
|
send_request(o);
|
||||||
|
|
||||||
|
// set timer
|
||||||
|
BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_INITIAL_WAITRECV);
|
||||||
|
|
||||||
|
// set zero missed
|
||||||
|
o->num_missed = 0;
|
||||||
|
|
||||||
|
// set state initial
|
||||||
|
o->state = STATE_INITIAL;
|
||||||
|
|
||||||
|
DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor));
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail1:
|
||||||
|
BDatagram_Free(&o->dgram);
|
||||||
|
fail0:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BArpProbe_Free (BArpProbe *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
DebugError_Free(&o->d_err);
|
||||||
|
|
||||||
|
// free timer
|
||||||
|
BReactor_RemoveTimer(o->reactor, &o->timer);
|
||||||
|
|
||||||
|
// free recv interface
|
||||||
|
BDatagram_RecvAsync_Free(&o->dgram);
|
||||||
|
|
||||||
|
// free send interface
|
||||||
|
BDatagram_SendAsync_Free(&o->dgram);
|
||||||
|
|
||||||
|
// free dgram
|
||||||
|
BDatagram_Free(&o->dgram);
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
/**
|
||||||
|
* @file BArpProbe.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_BARPPROBE_H
|
||||||
|
#define BADVPN_BARPPROBE_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <misc/debugerror.h>
|
||||||
|
#include <misc/arp_proto.h>
|
||||||
|
#include <misc/ethernet_proto.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <system/BDatagram.h>
|
||||||
|
#include <system/BReactor.h>
|
||||||
|
|
||||||
|
#define BARPPROBE_INITIAL_WAITRECV 1000
|
||||||
|
#define BARPPROBE_INITIAL_NUM_ATTEMPTS 6
|
||||||
|
#define BARPPROBE_NOEXIST_WAITRECV 15000
|
||||||
|
#define BARPPROBE_EXIST_WAITSEND 15000
|
||||||
|
#define BARPPROBE_EXIST_WAITRECV 10000
|
||||||
|
#define BARPPROBE_EXIST_NUM_NOREPLY 2
|
||||||
|
#define BARPPROBE_EXIST_PANIC_WAITRECV 1000
|
||||||
|
#define BARPPROBE_EXIST_PANIC_NUM_NOREPLY 6
|
||||||
|
|
||||||
|
#define BARPPROBE_EVENT_EXIST 1
|
||||||
|
#define BARPPROBE_EVENT_NOEXIST 2
|
||||||
|
#define BARPPROBE_EVENT_ERROR 3
|
||||||
|
|
||||||
|
typedef void (*BArpProbe_handler) (void *user, int event);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t addr;
|
||||||
|
BReactor *reactor;
|
||||||
|
void *user;
|
||||||
|
BArpProbe_handler handler;
|
||||||
|
BDatagram dgram;
|
||||||
|
uint8_t if_mac[6];
|
||||||
|
PacketPassInterface *send_if;
|
||||||
|
int send_sending;
|
||||||
|
struct arp_packet send_packet;
|
||||||
|
PacketRecvInterface *recv_if;
|
||||||
|
struct arp_packet recv_packet;
|
||||||
|
BTimer timer;
|
||||||
|
int state;
|
||||||
|
int num_missed;
|
||||||
|
DebugError d_err;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} BArpProbe;
|
||||||
|
|
||||||
|
int BArpProbe_Init (BArpProbe *o, const char *ifname, uint32_t addr, BReactor *reactor, void *user, BArpProbe_handler handler) WARN_UNUSED;
|
||||||
|
void BArpProbe_Free (BArpProbe *o);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1 @@
|
||||||
|
badvpn_add_library(arpprobe "base;system;flow" "" BArpProbe.c)
|
|
@ -0,0 +1,324 @@
|
||||||
|
.TH badvpn 7 "6 October 2010"
|
||||||
|
.SH NAME
|
||||||
|
BadVPN - peer-to-peer VPN system
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.P
|
||||||
|
BadVPN is a peer-to-peer VPN system. It provides a Layer 2 (Ethernet) network between
|
||||||
|
the peers (VPN network nodes). The peers connect to a central server which acts as a chat
|
||||||
|
server for them to establish direct connections between each other (data connections).
|
||||||
|
These connections are used for transferring network data (Ethernet frames).
|
||||||
|
.SS "Features"
|
||||||
|
.P
|
||||||
|
.B "Data connections"
|
||||||
|
.P
|
||||||
|
Peers can transfer network data either over UDP or TCP. For both there are ways of
|
||||||
|
securing the data (see below).
|
||||||
|
.P
|
||||||
|
.B "IPv6 support"
|
||||||
|
.P
|
||||||
|
IPv6 can be used for both server connections and data connections, alongside with IPv4.
|
||||||
|
Additionally, both can be combined to allow gradual migration to IPv6.
|
||||||
|
.P
|
||||||
|
.B "Address selection"
|
||||||
|
.P
|
||||||
|
Because NATs and firewalls are widespread, it is harder for peer-to-peer services to operate.
|
||||||
|
In general, for two computers to be able to communicate, one computer must
|
||||||
|
.I bind
|
||||||
|
to one of its addresses, and the other computer must
|
||||||
|
.I connect
|
||||||
|
to the computer that binded (both for TCP and UDP). In a network with point-to-point
|
||||||
|
connectivity, the connecting computer can connect to the same address as the binding computer
|
||||||
|
bound to, so it is sufficient for the binding computer to send its address to the connecting
|
||||||
|
computer. However, NATs and firewalls break point-to-point connectivity. When a network is
|
||||||
|
behind a NAT, it is, by default, impossible for computers outside of that network to connect
|
||||||
|
to computers inside the network. This is because computers inside the network have no externally
|
||||||
|
visible IP address, and only communicate with the outside world through the external IP address
|
||||||
|
of the NAT router. It is however possible to manually configure the NAT router to
|
||||||
|
.I forward
|
||||||
|
a specific port number on its external IP address to a specific computer inside the network.
|
||||||
|
This makes it possible for a computer outside of the network to connect to a computer inside
|
||||||
|
a network, however, it must connect to the external address of the NAT router (rather than
|
||||||
|
the address the computer inside bound to, which is its internal address). So there needs
|
||||||
|
to be some way for the connecting peer to know what address to connect to.
|
||||||
|
.P
|
||||||
|
BadVPN solves this problem with so-called
|
||||||
|
.IR "address scopes" "."
|
||||||
|
The peer that binds must have a list of external addresses for each address it can bind to,
|
||||||
|
possibly ordered from best to worst. Each external address has its scope name. A scope name
|
||||||
|
represents part of a network from which an external address can be reached. On the other hand,
|
||||||
|
the peer that connects must have a list of scopes which it can reach. When a peer binds to an
|
||||||
|
address, it sends the other peer a list of external addresses along with scope names. That peer
|
||||||
|
than chooses the first external address whose scope it recognizes and attempts to connect to it
|
||||||
|
(if there is one).
|
||||||
|
.P
|
||||||
|
BadVPN also allows a peer to have multiple addresses for binding to. It is possible to specify
|
||||||
|
both an IPv4 and an IPv6 address to work in a multi-protocol environment.
|
||||||
|
.P
|
||||||
|
.B "Relaying"
|
||||||
|
.P
|
||||||
|
BadVPN can be configured to allow pairs of peers that cannot communicate directly (i.e. because of
|
||||||
|
NATs or firewalls) to relay network data through a third peer. Relaying is only attempted if
|
||||||
|
none of the two peers recognize any of the other peer's external addresses (or there are none).
|
||||||
|
For relaying to work, for each of the two peers (P1, other one P2) there must be at least one
|
||||||
|
third peer (R) that P1 it is allowed to relay through and can communicate directly with, and all
|
||||||
|
such peers R must be able to communicate directly with P2.
|
||||||
|
.P
|
||||||
|
.B "IGMP snooping"
|
||||||
|
.P
|
||||||
|
BadVPN nodes perform IGMP snooping in order to efficiently deliver multicast frames. For example,
|
||||||
|
this makes it possible to use BadVPN as a tunnel into an IPTV network of an Internet Service Provider
|
||||||
|
for you to watch TV from wherever you want (given sufficient link quality).
|
||||||
|
.P
|
||||||
|
.B "Code quality"
|
||||||
|
.P
|
||||||
|
BadVPN has great focus on code quality and reliability. BadVPN is written in the C programming
|
||||||
|
language. It is a single-threaded event-driven program. This allows for low resource usage and
|
||||||
|
fast response times. Even though C is a relatively low-level language, the programs are made of
|
||||||
|
small, highly cohesive and loosely coupled modules that are combined into a complete program on
|
||||||
|
a high level. Modules are accesed and communicate through small, simple and to-the-point interfaces.
|
||||||
|
It utilizes a flow-based design which greatly simplifies processing of data and input and output
|
||||||
|
of the programs.
|
||||||
|
.SS "Security features"
|
||||||
|
.P
|
||||||
|
BadVPN contains many security features, all of which are optional. The included security
|
||||||
|
features are described here.
|
||||||
|
.P
|
||||||
|
.B TLS for client-server connections
|
||||||
|
.P
|
||||||
|
It is possible for the peers to communicate with the chat server securely with TLS. It is
|
||||||
|
highly recommended that this feature is used if any security whatsoever is needed. Not
|
||||||
|
using it renders all other security features useless, since clients exchange keys
|
||||||
|
unencrypted via the server. When enabled, the chat server requires each client to identify
|
||||||
|
itself with a certificate.
|
||||||
|
.P
|
||||||
|
BadVPN uses Mozilla's NSS library for TLS support. This means that the required certificates
|
||||||
|
and keys must be available in a NSS database. The database and certificates can be
|
||||||
|
generated with the
|
||||||
|
.B certutil
|
||||||
|
command. See the examples section on how to generate and distribute the certificates.
|
||||||
|
.P
|
||||||
|
.B TLS for peer messaging
|
||||||
|
.P
|
||||||
|
If TLS is being used for client-server connections, it will also be used between each pair of
|
||||||
|
peers communicating via the server, on top of the TLS connections to the server. This secures
|
||||||
|
the messages from the server itself. It is important because the messages may include
|
||||||
|
encryption keys and other private data.
|
||||||
|
.P
|
||||||
|
.B TLS for TCP data connections
|
||||||
|
.P
|
||||||
|
If TCP is used for data connections between the peers, the data connections can be secured
|
||||||
|
with TLS. This requires using TLS for client-server connections. The clients need to trust
|
||||||
|
each others' certificates to be able to connect. Additionally, each client must identify to
|
||||||
|
its peers with the same certificates it used for connecting to the server.
|
||||||
|
.P
|
||||||
|
.B Encryption for UDP data connections
|
||||||
|
.P
|
||||||
|
If UDP is used for data connections, it is possible for each pair of peers to encrypt their
|
||||||
|
UDP packets with a symmetric block cipher. Note that the encryption keys are transmitted
|
||||||
|
through the server unencrypted, so for this to be useful, server connections must be secured
|
||||||
|
with TLS. The encryption aims to prevent third parties from seeing the real contents of
|
||||||
|
the network data being transfered.
|
||||||
|
.P
|
||||||
|
.B Hashes for UDP data connections
|
||||||
|
.P
|
||||||
|
If UDP is used for data connections, it is possible to include hashes in packets. Note that
|
||||||
|
hashes are only useful together with encryption. If enabled, the hash is calculated on the
|
||||||
|
packet with the hash field zeroed and then written to the hash field. Hashes are calculated
|
||||||
|
and included before encryption (if enabled). Combined with encryption, hashes aim to prevent
|
||||||
|
third parties from tampering with the packets and injecting them into the network.
|
||||||
|
.P
|
||||||
|
.B One-time passwords for UDP data connections
|
||||||
|
.P
|
||||||
|
If UDP is used for data connections, it is possible to include one-time passwords in packets.
|
||||||
|
Note that for this to be useful, server connections must be secured with TLS.
|
||||||
|
One-time passwords are generated from a seed value by encrypting zero data with a block cipher.
|
||||||
|
The seed contains the encryption key for the block cipher and the initialization vector.
|
||||||
|
Only a fixed number of passwords are used from a single seed. The peers exchange seeds through
|
||||||
|
the server. One-time passwords aim to prevent replay attacks.
|
||||||
|
.P
|
||||||
|
.B Control over peer communication
|
||||||
|
.P
|
||||||
|
It is possible to instruct the chat server to only allow certain peers to communicate. This
|
||||||
|
will break end-to-end connectivity in the virtual network. It is useful in certain cases
|
||||||
|
to improve security, for example when the VPN is used only to allow clients to securely connect
|
||||||
|
to a central service.
|
||||||
|
.SH "EXAMPLES"
|
||||||
|
.SS "Setting up certificates"
|
||||||
|
.P
|
||||||
|
If you want to use TLS for server connections (recommended), the server and all the peers will
|
||||||
|
need certificates. This section explains how to generate and distribute the certificates using
|
||||||
|
NSS command line tools.
|
||||||
|
.P
|
||||||
|
.B Setting up the Certificate Authority (CA)
|
||||||
|
.P
|
||||||
|
On the system that will host the CA, create a NSS database for the CA and generate a CA certificate
|
||||||
|
valid for 24 months:
|
||||||
|
.P
|
||||||
|
vpnca $ certutil -d sql:/home/vpnca/nssdb -N
|
||||||
|
.br
|
||||||
|
vpnca $ certutil -d sql:/home/vpnca/nssdb -S -n "vpnca" -s "CN=vpnca" -t "TC,," -x -2 -v 24
|
||||||
|
.br
|
||||||
|
> Is this a CA certificate [y/N]? y
|
||||||
|
.br
|
||||||
|
> Enter the path length constraint, enter to skip [<0 for unlimited path]: > -1
|
||||||
|
.br
|
||||||
|
> Is this a critical extension [y/N]? n
|
||||||
|
.P
|
||||||
|
Export the public CA certificate (this file is public):
|
||||||
|
.P
|
||||||
|
vpnca $ certutil -d sql:/home/vpnca/nssdb -L -n vpnca -a > ca.pem
|
||||||
|
.P
|
||||||
|
.B Setting up the server certificate
|
||||||
|
.P
|
||||||
|
On the CA system, generate a certificate for the server valid for 24 months, with TLS server usage context:
|
||||||
|
.P
|
||||||
|
vpnca $ certutil -d sql:/home/vpnca/nssdb -S -n "<insert_server_name>" -s "CN=<insert_server_name>" -c "vpnca" -t ",," -2 -6 -v 24
|
||||||
|
.br
|
||||||
|
> 0
|
||||||
|
.br
|
||||||
|
> -1
|
||||||
|
.br
|
||||||
|
> Is this a critical extension [y/N]? n
|
||||||
|
.br
|
||||||
|
> Is this a CA certificate [y/N]? n
|
||||||
|
.br
|
||||||
|
> Enter the path length constraint, enter to skip [<0 for unlimited path]: >
|
||||||
|
.br
|
||||||
|
> Is this a critical extension [y/N]? n
|
||||||
|
.P
|
||||||
|
Export the server certificate to a PKCS#12 file (this file must be kept secret):
|
||||||
|
.P
|
||||||
|
vpnca $ pk12util -d sql:/home/vpnca/nssdb -o server.p12 -n "<insert_server_name>"
|
||||||
|
.P
|
||||||
|
On the system that will run the server, create a NSS database and import the CA certificate
|
||||||
|
and the server cerificate:
|
||||||
|
.P
|
||||||
|
vpnserver $ certutil -d sql:/home/vpnserver/nssdb -N
|
||||||
|
.br
|
||||||
|
vpnserver $ certutil -d sql:/home/vpnserver/nssdb -A -t "CT,," -n "vpnca" -i /path/to/ca.pem
|
||||||
|
.br
|
||||||
|
vpnserver $ pk12util -d sql:/home/vpnserver/nssdb -i /path/to/server.p12
|
||||||
|
.P
|
||||||
|
.B Setting up peer certificates
|
||||||
|
.P
|
||||||
|
On the CA system, generate a certificate for the peer valid for 24 months, with TLS client and
|
||||||
|
TLS server usage contexts:
|
||||||
|
.P
|
||||||
|
vpnca $ certutil -d sql:/home/vpnca/nssdb -S -n "peer-<insert_name>" -s "CN=peer-<insert_name>" -c "vpnca" -t ",," -2 -6 -v 24
|
||||||
|
.br
|
||||||
|
> 0
|
||||||
|
.br
|
||||||
|
> 1
|
||||||
|
.br
|
||||||
|
> -1
|
||||||
|
.br
|
||||||
|
> Is this a critical extension [y/N]? n
|
||||||
|
.br
|
||||||
|
> Is this a CA certificate [y/N]? n
|
||||||
|
.br
|
||||||
|
> Enter the path length constraint, enter to skip [<0 for unlimited path]: >
|
||||||
|
.br
|
||||||
|
> Is this a critical extension [y/N]? n
|
||||||
|
.P
|
||||||
|
Export the peer certificate to a PKCS#12 file (this file must be kept secret):
|
||||||
|
.P
|
||||||
|
vpnca $ pk12util -d sql:/home/vpnca/nssdb -o peer-<insert_name>.p12 -n "peer-<insert_name>"
|
||||||
|
.P
|
||||||
|
On the system that will run the VPN client, create a NSS database and import the CA certificate
|
||||||
|
and the peer cerificate:
|
||||||
|
.P
|
||||||
|
vpnclient $ certutil -d sql:/home/vpnclient/nssdb -N
|
||||||
|
.br
|
||||||
|
vpnclient $ certutil -d sql:/home/vpnclient/nssdb -A -t "CT,," -n "vpnca" -i /path/to/ca.pem
|
||||||
|
.br
|
||||||
|
vpnclient $ pk12util -d sql:/home/vpnclient/nssdb -i /path/to/peer-<insert_name>.p12
|
||||||
|
.SS "Setting up TAP devices"
|
||||||
|
.P
|
||||||
|
You need to create and configure TAP devices on all computers that will participate in the virtual network
|
||||||
|
(i.e. run the client program). See
|
||||||
|
.BR badvpn-client (8),
|
||||||
|
section `TAP DEVICE CONFIGURATION` for details.
|
||||||
|
.SS "Example: Local IPv4 network, UDP transport, zero security"
|
||||||
|
.P
|
||||||
|
.B Starting the server:
|
||||||
|
.P
|
||||||
|
badvpn-server --listen-addr 0.0.0.0:7000
|
||||||
|
.P
|
||||||
|
.B Starting the peers:
|
||||||
|
.P
|
||||||
|
badvpn-client
|
||||||
|
.RS
|
||||||
|
--server-addr <insert_server_local_address>:7000
|
||||||
|
.br
|
||||||
|
--transport-mode udp --encryption-mode none --hash-mode none
|
||||||
|
.br
|
||||||
|
--scope local1
|
||||||
|
.br
|
||||||
|
--bind-addr 0.0.0.0:8000 --num-ports 30 --ext-addr {server_reported}:8000 local1
|
||||||
|
.br
|
||||||
|
--tapdev tap0
|
||||||
|
.RE
|
||||||
|
.SS "Example: Adding TLS and UDP security"
|
||||||
|
.P
|
||||||
|
.B Starting the server (other options as above):
|
||||||
|
.P
|
||||||
|
badvpn-server ...
|
||||||
|
.RS
|
||||||
|
--ssl --nssdb sql:/home/vpnserver/nssdb --server-cert-name "<insert_server_name>"
|
||||||
|
.RE
|
||||||
|
.P
|
||||||
|
.B Starting the peers (other options as above):
|
||||||
|
.P
|
||||||
|
badvpn-client ...
|
||||||
|
.RS
|
||||||
|
--ssl --nssdb sql:/home/vpnclient/nssdb --client-cert-name "peer-<insert_name>"
|
||||||
|
.br
|
||||||
|
--encryption-mode blowfish --hash-mode md5 --otp blowfish 3000 2000
|
||||||
|
.RE
|
||||||
|
.SS "Example: Multiple local networks behind NATs, all connected to the Internet"
|
||||||
|
.P
|
||||||
|
For each peer in the existing local network, configure the NAT router to forward its
|
||||||
|
range of ports to it (assuming their port ranges do not overlap). The clients also need
|
||||||
|
to know the external IP address of the NAT router. If you don't have a static one,
|
||||||
|
you'll need to discover it before starting the clients. Also forward the server port to
|
||||||
|
the server.
|
||||||
|
.P
|
||||||
|
.B Starting the peers in the local network (other options as above):
|
||||||
|
.P
|
||||||
|
badvpn-client
|
||||||
|
.RS
|
||||||
|
.RB "..."
|
||||||
|
.br
|
||||||
|
--scope internet
|
||||||
|
.br
|
||||||
|
.RB "..."
|
||||||
|
.br
|
||||||
|
--ext-addr <insert_NAT_routers_external_IP>:<insert_start_of_forwarded_port_range> internet
|
||||||
|
.br
|
||||||
|
.RB "..."
|
||||||
|
.RE
|
||||||
|
.P
|
||||||
|
The --ext-addr option applies to the previously specified --bind-addr option, and must come after
|
||||||
|
the first --ext-addr option which specifies a local address.
|
||||||
|
.P
|
||||||
|
Now perform a similar setup in some other local network behind a NAT. However:
|
||||||
|
.br
|
||||||
|
- Don't set up a new server, instead make the peers connect to the existing server in the first
|
||||||
|
local network.
|
||||||
|
.br
|
||||||
|
- You can't use {server_reported} for the local address --ext-addr options, because the server
|
||||||
|
would report the NAT router's external address rather than the peer's internal address. Instead
|
||||||
|
each peer has to know its internal IP address.
|
||||||
|
.br
|
||||||
|
- Use a different scope name for it, e.g. "local2" instead of "local1".
|
||||||
|
.P
|
||||||
|
If setup correctly, all peers will be able to communicate: those in the same local network will
|
||||||
|
communicate directly through local addresses, and those in different local networks will
|
||||||
|
communicate through the Internet.
|
||||||
|
.SH "PROTOCOL"
|
||||||
|
The protocols used in BadVPN are described in the source code in the protocol/ directory.
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.BR badvpn-server (8),
|
||||||
|
.BR badvpn-client (8)
|
||||||
|
.SH AUTHORS
|
||||||
|
Ambroz Bizjak <ambrop7@gmail.com>
|
|
@ -0,0 +1,96 @@
|
||||||
|
/**
|
||||||
|
* @file BLog.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "BLog.h"
|
||||||
|
|
||||||
|
#ifndef BADVPN_PLUGIN
|
||||||
|
|
||||||
|
struct _BLog_channel blog_channel_list[] = {
|
||||||
|
#include <generated/blog_channels_list.h>
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _BLog_global blog_global = {
|
||||||
|
#ifndef NDEBUG
|
||||||
|
0
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// keep in sync with level numbers in BLog.h!
|
||||||
|
static char *level_names[] = { NULL, "ERROR", "WARNING", "NOTICE", "INFO", "DEBUG" };
|
||||||
|
|
||||||
|
static void stdout_log (int channel, int level, const char *msg)
|
||||||
|
{
|
||||||
|
fprintf(stdout, "%s(%s): %s\n", level_names[level], blog_global.channels[channel].name, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stderr_log (int channel, int level, const char *msg)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s(%s): %s\n", level_names[level], blog_global.channels[channel].name, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stdout_stderr_free (void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLog_InitStdout (void)
|
||||||
|
{
|
||||||
|
BLog_Init(stdout_log, stdout_stderr_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLog_InitStderr (void)
|
||||||
|
{
|
||||||
|
BLog_Init(stderr_log, stdout_stderr_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==== PSIPHON ====
|
||||||
|
#ifdef PSIPHON
|
||||||
|
|
||||||
|
void PsiphonLog(const char *level, const char *channel, const char *msg);
|
||||||
|
|
||||||
|
static void psiphon_log (int channel, int level, const char *msg)
|
||||||
|
{
|
||||||
|
PsiphonLog(level_names[level], blog_global.channels[channel].name, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void psiphon_free (void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLog_InitPsiphon (void)
|
||||||
|
{
|
||||||
|
BLog_Init(psiphon_log, psiphon_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
// ==== PSIPHON ====
|
|
@ -0,0 +1,402 @@
|
||||||
|
/**
|
||||||
|
* @file BLog.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @section DESCRIPTION
|
||||||
|
*
|
||||||
|
* A global object for logging.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_BLOG_H
|
||||||
|
#define BADVPN_BLOG_H
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <base/BMutex.h>
|
||||||
|
|
||||||
|
// auto-generated channel numbers and number of channels
|
||||||
|
#include <generated/blog_channels_defines.h>
|
||||||
|
|
||||||
|
// keep in sync with level names in BLog.c!
|
||||||
|
#define BLOG_ERROR 1
|
||||||
|
#define BLOG_WARNING 2
|
||||||
|
#define BLOG_NOTICE 3
|
||||||
|
#define BLOG_INFO 4
|
||||||
|
#define BLOG_DEBUG 5
|
||||||
|
|
||||||
|
#define BLog(...) BLog_LogToChannel(BLOG_CURRENT_CHANNEL, __VA_ARGS__)
|
||||||
|
#define BContextLog(context, ...) BLog_ContextLog((context), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
|
||||||
|
#define BLOG_CCCC(context) BLog_MakeChannelContext((context), BLOG_CURRENT_CHANNEL)
|
||||||
|
|
||||||
|
typedef void (*_BLog_log_func) (int channel, int level, const char *msg);
|
||||||
|
typedef void (*_BLog_free_func) (void);
|
||||||
|
|
||||||
|
struct _BLog_channel {
|
||||||
|
const char *name;
|
||||||
|
int loglevel;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _BLog_global {
|
||||||
|
#ifndef NDEBUG
|
||||||
|
int initialized; // initialized statically
|
||||||
|
#endif
|
||||||
|
struct _BLog_channel channels[BLOG_NUM_CHANNELS];
|
||||||
|
_BLog_log_func log_func;
|
||||||
|
_BLog_free_func free_func;
|
||||||
|
BMutex mutex;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
int logging;
|
||||||
|
#endif
|
||||||
|
char logbuf[2048];
|
||||||
|
int logbuf_pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct _BLog_channel blog_channel_list[];
|
||||||
|
extern struct _BLog_global blog_global;
|
||||||
|
|
||||||
|
typedef void (*BLog_logfunc) (void *);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
BLog_logfunc logfunc;
|
||||||
|
void *logfunc_user;
|
||||||
|
} BLogContext;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
BLogContext context;
|
||||||
|
int channel;
|
||||||
|
} BLogChannelContext;
|
||||||
|
|
||||||
|
static int BLogGlobal_GetChannelByName (const char *channel_name);
|
||||||
|
|
||||||
|
static void BLog_Init (_BLog_log_func log_func, _BLog_free_func free_func);
|
||||||
|
static void BLog_Free (void);
|
||||||
|
static void BLog_SetChannelLoglevel (int channel, int loglevel);
|
||||||
|
static int BLog_WouldLog (int channel, int level);
|
||||||
|
static void BLog_Begin (void);
|
||||||
|
static void BLog_AppendVarArg (const char *fmt, va_list vl);
|
||||||
|
static void BLog_Append (const char *fmt, ...);
|
||||||
|
static void BLog_AppendBytes (const char *data, size_t len);
|
||||||
|
static void BLog_Finish (int channel, int level);
|
||||||
|
static void BLog_LogToChannelVarArg (int channel, int level, const char *fmt, va_list vl);
|
||||||
|
static void BLog_LogToChannel (int channel, int level, const char *fmt, ...);
|
||||||
|
static void BLog_LogViaFuncVarArg (BLog_logfunc func, void *arg, int channel, int level, const char *fmt, va_list vl);
|
||||||
|
static void BLog_LogViaFunc (BLog_logfunc func, void *arg, int channel, int level, const char *fmt, ...);
|
||||||
|
static BLogContext BLog_RootContext (void);
|
||||||
|
static BLogContext BLog_MakeContext (BLog_logfunc logfunc, void *logfunc_user);
|
||||||
|
static void BLog_ContextLogVarArg (BLogContext context, int channel, int level, const char *fmt, va_list vl);
|
||||||
|
static void BLog_ContextLog (BLogContext context, int channel, int level, const char *fmt, ...);
|
||||||
|
static BLogChannelContext BLog_MakeChannelContext (BLogContext context, int channel);
|
||||||
|
static void BLog_ChannelContextLogVarArg (BLogChannelContext ccontext, int level, const char *fmt, va_list vl);
|
||||||
|
static void BLog_ChannelContextLog (BLogChannelContext ccontext, int level, const char *fmt, ...);
|
||||||
|
|
||||||
|
void BLog_InitStdout (void);
|
||||||
|
void BLog_InitStderr (void);
|
||||||
|
|
||||||
|
// PSIPHON
|
||||||
|
void BLog_InitPsiphon (void);
|
||||||
|
|
||||||
|
int BLogGlobal_GetChannelByName (const char *channel_name)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < BLOG_NUM_CHANNELS; i++) {
|
||||||
|
if (!strcmp(blog_channel_list[i].name, channel_name)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLog_Init (_BLog_log_func log_func, _BLog_free_func free_func)
|
||||||
|
{
|
||||||
|
ASSERT(!blog_global.initialized)
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
blog_global.initialized = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// initialize channels
|
||||||
|
memcpy(blog_global.channels, blog_channel_list, BLOG_NUM_CHANNELS * sizeof(struct _BLog_channel));
|
||||||
|
|
||||||
|
blog_global.log_func = log_func;
|
||||||
|
blog_global.free_func = free_func;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
blog_global.logging = 0;
|
||||||
|
#endif
|
||||||
|
blog_global.logbuf_pos = 0;
|
||||||
|
blog_global.logbuf[0] = '\0';
|
||||||
|
|
||||||
|
ASSERT_FORCE(BMutex_Init(&blog_global.mutex))
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLog_Free (void)
|
||||||
|
{
|
||||||
|
ASSERT(blog_global.initialized)
|
||||||
|
#ifndef NDEBUG
|
||||||
|
ASSERT(!blog_global.logging)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
BMutex_Free(&blog_global.mutex);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
blog_global.initialized = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
blog_global.free_func();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLog_SetChannelLoglevel (int channel, int loglevel)
|
||||||
|
{
|
||||||
|
ASSERT(blog_global.initialized)
|
||||||
|
ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS)
|
||||||
|
ASSERT(loglevel >= 0 && loglevel <= BLOG_DEBUG)
|
||||||
|
|
||||||
|
blog_global.channels[channel].loglevel = loglevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BLog_WouldLog (int channel, int level)
|
||||||
|
{
|
||||||
|
ASSERT(blog_global.initialized)
|
||||||
|
ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS)
|
||||||
|
ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG)
|
||||||
|
|
||||||
|
return (level <= blog_global.channels[channel].loglevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLog_Begin (void)
|
||||||
|
{
|
||||||
|
ASSERT(blog_global.initialized)
|
||||||
|
|
||||||
|
BMutex_Lock(&blog_global.mutex);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
ASSERT(!blog_global.logging)
|
||||||
|
blog_global.logging = 1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLog_AppendVarArg (const char *fmt, va_list vl)
|
||||||
|
{
|
||||||
|
ASSERT(blog_global.initialized)
|
||||||
|
#ifndef NDEBUG
|
||||||
|
ASSERT(blog_global.logging)
|
||||||
|
#endif
|
||||||
|
ASSERT(blog_global.logbuf_pos >= 0)
|
||||||
|
ASSERT(blog_global.logbuf_pos < sizeof(blog_global.logbuf))
|
||||||
|
|
||||||
|
int w = vsnprintf(blog_global.logbuf + blog_global.logbuf_pos, sizeof(blog_global.logbuf) - blog_global.logbuf_pos, fmt, vl);
|
||||||
|
|
||||||
|
if (w >= sizeof(blog_global.logbuf) - blog_global.logbuf_pos) {
|
||||||
|
blog_global.logbuf_pos = sizeof(blog_global.logbuf) - 1;
|
||||||
|
} else {
|
||||||
|
blog_global.logbuf_pos += w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLog_Append (const char *fmt, ...)
|
||||||
|
{
|
||||||
|
ASSERT(blog_global.initialized)
|
||||||
|
#ifndef NDEBUG
|
||||||
|
ASSERT(blog_global.logging)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
va_list vl;
|
||||||
|
va_start(vl, fmt);
|
||||||
|
BLog_AppendVarArg(fmt, vl);
|
||||||
|
va_end(vl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLog_AppendBytes (const char *data, size_t len)
|
||||||
|
{
|
||||||
|
ASSERT(blog_global.initialized)
|
||||||
|
#ifndef NDEBUG
|
||||||
|
ASSERT(blog_global.logging)
|
||||||
|
#endif
|
||||||
|
ASSERT(blog_global.logbuf_pos >= 0)
|
||||||
|
ASSERT(blog_global.logbuf_pos < sizeof(blog_global.logbuf))
|
||||||
|
|
||||||
|
size_t avail = (sizeof(blog_global.logbuf) - 1) - blog_global.logbuf_pos;
|
||||||
|
len = (len > avail ? avail : len);
|
||||||
|
|
||||||
|
memcpy(blog_global.logbuf + blog_global.logbuf_pos, data, len);
|
||||||
|
blog_global.logbuf_pos += len;
|
||||||
|
blog_global.logbuf[blog_global.logbuf_pos] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLog_Finish (int channel, int level)
|
||||||
|
{
|
||||||
|
ASSERT(blog_global.initialized)
|
||||||
|
#ifndef NDEBUG
|
||||||
|
ASSERT(blog_global.logging)
|
||||||
|
#endif
|
||||||
|
ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS)
|
||||||
|
ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG)
|
||||||
|
ASSERT(BLog_WouldLog(channel, level))
|
||||||
|
|
||||||
|
ASSERT(blog_global.logbuf_pos >= 0)
|
||||||
|
ASSERT(blog_global.logbuf_pos < sizeof(blog_global.logbuf))
|
||||||
|
ASSERT(blog_global.logbuf[blog_global.logbuf_pos] == '\0')
|
||||||
|
|
||||||
|
blog_global.log_func(channel, level, blog_global.logbuf);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
blog_global.logging = 0;
|
||||||
|
#endif
|
||||||
|
blog_global.logbuf_pos = 0;
|
||||||
|
blog_global.logbuf[0] = '\0';
|
||||||
|
|
||||||
|
BMutex_Unlock(&blog_global.mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLog_LogToChannelVarArg (int channel, int level, const char *fmt, va_list vl)
|
||||||
|
{
|
||||||
|
ASSERT(blog_global.initialized)
|
||||||
|
ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS)
|
||||||
|
ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG)
|
||||||
|
|
||||||
|
if (!BLog_WouldLog(channel, level)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BLog_Begin();
|
||||||
|
BLog_AppendVarArg(fmt, vl);
|
||||||
|
BLog_Finish(channel, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLog_LogToChannel (int channel, int level, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
ASSERT(blog_global.initialized)
|
||||||
|
ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS)
|
||||||
|
ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG)
|
||||||
|
|
||||||
|
if (!BLog_WouldLog(channel, level)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
va_list vl;
|
||||||
|
va_start(vl, fmt);
|
||||||
|
|
||||||
|
BLog_Begin();
|
||||||
|
BLog_AppendVarArg(fmt, vl);
|
||||||
|
BLog_Finish(channel, level);
|
||||||
|
|
||||||
|
va_end(vl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLog_LogViaFuncVarArg (BLog_logfunc func, void *arg, int channel, int level, const char *fmt, va_list vl)
|
||||||
|
{
|
||||||
|
ASSERT(blog_global.initialized)
|
||||||
|
ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS)
|
||||||
|
ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG)
|
||||||
|
|
||||||
|
if (!BLog_WouldLog(channel, level)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BLog_Begin();
|
||||||
|
func(arg);
|
||||||
|
BLog_AppendVarArg(fmt, vl);
|
||||||
|
BLog_Finish(channel, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLog_LogViaFunc (BLog_logfunc func, void *arg, int channel, int level, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
ASSERT(blog_global.initialized)
|
||||||
|
ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS)
|
||||||
|
ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG)
|
||||||
|
|
||||||
|
if (!BLog_WouldLog(channel, level)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
va_list vl;
|
||||||
|
va_start(vl, fmt);
|
||||||
|
|
||||||
|
BLog_Begin();
|
||||||
|
func(arg);
|
||||||
|
BLog_AppendVarArg(fmt, vl);
|
||||||
|
BLog_Finish(channel, level);
|
||||||
|
|
||||||
|
va_end(vl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BLog__root_logfunc (void *unused)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static BLogContext BLog_RootContext (void)
|
||||||
|
{
|
||||||
|
return BLog_MakeContext(BLog__root_logfunc, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BLogContext BLog_MakeContext (BLog_logfunc logfunc, void *logfunc_user)
|
||||||
|
{
|
||||||
|
ASSERT(logfunc)
|
||||||
|
|
||||||
|
BLogContext context;
|
||||||
|
context.logfunc = logfunc;
|
||||||
|
context.logfunc_user = logfunc_user;
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BLog_ContextLogVarArg (BLogContext context, int channel, int level, const char *fmt, va_list vl)
|
||||||
|
{
|
||||||
|
BLog_LogViaFuncVarArg(context.logfunc, context.logfunc_user, channel, level, fmt, vl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BLog_ContextLog (BLogContext context, int channel, int level, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list vl;
|
||||||
|
va_start(vl, fmt);
|
||||||
|
BLog_ContextLogVarArg(context, channel, level, fmt, vl);
|
||||||
|
va_end(vl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BLogChannelContext BLog_MakeChannelContext (BLogContext context, int channel)
|
||||||
|
{
|
||||||
|
BLogChannelContext ccontext;
|
||||||
|
ccontext.context = context;
|
||||||
|
ccontext.channel = channel;
|
||||||
|
return ccontext;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BLog_ChannelContextLogVarArg (BLogChannelContext ccontext, int level, const char *fmt, va_list vl)
|
||||||
|
{
|
||||||
|
BLog_ContextLogVarArg(ccontext.context, ccontext.channel, level, fmt, vl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BLog_ChannelContextLog (BLogChannelContext ccontext, int level, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list vl;
|
||||||
|
va_start(vl, fmt);
|
||||||
|
BLog_ChannelContextLogVarArg(ccontext, level, fmt, vl);
|
||||||
|
va_end(vl);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,150 @@
|
||||||
|
/**
|
||||||
|
* @file BLog_syslog.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
|
||||||
|
#include "BLog_syslog.h"
|
||||||
|
|
||||||
|
static int resolve_facility (char *str, int *out)
|
||||||
|
{
|
||||||
|
if (!strcmp(str, "authpriv")) {
|
||||||
|
*out = LOG_AUTHPRIV;
|
||||||
|
}
|
||||||
|
else if (!strcmp(str, "cron")) {
|
||||||
|
*out = LOG_CRON;
|
||||||
|
}
|
||||||
|
else if (!strcmp(str, "daemon")) {
|
||||||
|
*out = LOG_DAEMON;
|
||||||
|
}
|
||||||
|
else if (!strcmp(str, "ftp")) {
|
||||||
|
*out = LOG_FTP;
|
||||||
|
}
|
||||||
|
else if (!strcmp(str, "local0")) {
|
||||||
|
*out = LOG_LOCAL0;
|
||||||
|
}
|
||||||
|
else if (!strcmp(str, "local1")) {
|
||||||
|
*out = LOG_LOCAL1;
|
||||||
|
}
|
||||||
|
else if (!strcmp(str, "local2")) {
|
||||||
|
*out = LOG_LOCAL2;
|
||||||
|
}
|
||||||
|
else if (!strcmp(str, "local3")) {
|
||||||
|
*out = LOG_LOCAL3;
|
||||||
|
}
|
||||||
|
else if (!strcmp(str, "local4")) {
|
||||||
|
*out = LOG_LOCAL4;
|
||||||
|
}
|
||||||
|
else if (!strcmp(str, "local5")) {
|
||||||
|
*out = LOG_LOCAL5;
|
||||||
|
}
|
||||||
|
else if (!strcmp(str, "local6")) {
|
||||||
|
*out = LOG_LOCAL6;
|
||||||
|
}
|
||||||
|
else if (!strcmp(str, "local7")) {
|
||||||
|
*out = LOG_LOCAL7;
|
||||||
|
}
|
||||||
|
else if (!strcmp(str, "lpr")) {
|
||||||
|
*out = LOG_LPR;
|
||||||
|
}
|
||||||
|
else if (!strcmp(str, "mail")) {
|
||||||
|
*out = LOG_MAIL;
|
||||||
|
}
|
||||||
|
else if (!strcmp(str, "news")) {
|
||||||
|
*out = LOG_NEWS;
|
||||||
|
}
|
||||||
|
else if (!strcmp(str, "syslog")) {
|
||||||
|
*out = LOG_SYSLOG;
|
||||||
|
}
|
||||||
|
else if (!strcmp(str, "user")) {
|
||||||
|
*out = LOG_USER;
|
||||||
|
}
|
||||||
|
else if (!strcmp(str, "uucp")) {
|
||||||
|
*out = LOG_UUCP;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int convert_level (int level)
|
||||||
|
{
|
||||||
|
ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG)
|
||||||
|
|
||||||
|
switch (level) {
|
||||||
|
case BLOG_ERROR:
|
||||||
|
return LOG_ERR;
|
||||||
|
case BLOG_WARNING:
|
||||||
|
return LOG_WARNING;
|
||||||
|
case BLOG_NOTICE:
|
||||||
|
return LOG_NOTICE;
|
||||||
|
case BLOG_INFO:
|
||||||
|
return LOG_INFO;
|
||||||
|
case BLOG_DEBUG:
|
||||||
|
return LOG_DEBUG;
|
||||||
|
default:
|
||||||
|
ASSERT(0)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
char ident[200];
|
||||||
|
} syslog_global;
|
||||||
|
|
||||||
|
static void syslog_log (int channel, int level, const char *msg)
|
||||||
|
{
|
||||||
|
syslog(convert_level(level), "%s: %s", blog_global.channels[channel].name, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void syslog_free (void)
|
||||||
|
{
|
||||||
|
closelog();
|
||||||
|
}
|
||||||
|
|
||||||
|
int BLog_InitSyslog (char *ident, char *facility_str)
|
||||||
|
{
|
||||||
|
int facility;
|
||||||
|
if (!resolve_facility(facility_str, &facility)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(syslog_global.ident, sizeof(syslog_global.ident), "%s", ident);
|
||||||
|
|
||||||
|
openlog(syslog_global.ident, 0, facility);
|
||||||
|
|
||||||
|
BLog_Init(syslog_log, syslog_free);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/**
|
||||||
|
* @file BLog_syslog.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @section DESCRIPTION
|
||||||
|
*
|
||||||
|
* BLog syslog backend.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_BLOG_SYSLOG_H
|
||||||
|
#define BADVPN_BLOG_SYSLOG_H
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <base/BLog.h>
|
||||||
|
|
||||||
|
int BLog_InitSyslog (char *ident, char *facility) WARN_UNUSED;
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,101 @@
|
||||||
|
/**
|
||||||
|
* @file BMutex.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_BMUTEX_H
|
||||||
|
#define BADVPN_BMUTEX_H
|
||||||
|
|
||||||
|
#if !defined(BADVPN_THREAD_SAFE) || (BADVPN_THREAD_SAFE != 0 && BADVPN_THREAD_SAFE != 1)
|
||||||
|
#error BADVPN_THREAD_SAFE is not defined or incorrect
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if BADVPN_THREAD_SAFE
|
||||||
|
#include <pthread.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
#if BADVPN_THREAD_SAFE
|
||||||
|
pthread_mutex_t pthread_mutex;
|
||||||
|
#endif
|
||||||
|
DebugObject d_obj;
|
||||||
|
} BMutex;
|
||||||
|
|
||||||
|
static int BMutex_Init (BMutex *o) WARN_UNUSED;
|
||||||
|
static void BMutex_Free (BMutex *o);
|
||||||
|
static void BMutex_Lock (BMutex *o);
|
||||||
|
static void BMutex_Unlock (BMutex *o);
|
||||||
|
|
||||||
|
static int BMutex_Init (BMutex *o)
|
||||||
|
{
|
||||||
|
#if BADVPN_THREAD_SAFE
|
||||||
|
if (pthread_mutex_init(&o->pthread_mutex, NULL) != 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BMutex_Free (BMutex *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
|
||||||
|
#if BADVPN_THREAD_SAFE
|
||||||
|
int res = pthread_mutex_destroy(&o->pthread_mutex);
|
||||||
|
B_USE(res)
|
||||||
|
ASSERT(res == 0)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BMutex_Lock (BMutex *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
#if BADVPN_THREAD_SAFE
|
||||||
|
int res = pthread_mutex_lock(&o->pthread_mutex);
|
||||||
|
B_USE(res)
|
||||||
|
ASSERT(res == 0)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BMutex_Unlock (BMutex *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
#if BADVPN_THREAD_SAFE
|
||||||
|
int res = pthread_mutex_unlock(&o->pthread_mutex);
|
||||||
|
B_USE(res)
|
||||||
|
ASSERT(res == 0)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,205 @@
|
||||||
|
/**
|
||||||
|
* @file BPending.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <misc/offset.h>
|
||||||
|
|
||||||
|
#include "BPending.h"
|
||||||
|
|
||||||
|
#include "BPending_list.h"
|
||||||
|
#include <structure/SLinkedList_impl.h>
|
||||||
|
|
||||||
|
void BPendingGroup_Init (BPendingGroup *g)
|
||||||
|
{
|
||||||
|
// init jobs list
|
||||||
|
BPending__List_Init(&g->jobs);
|
||||||
|
|
||||||
|
// init pending counter
|
||||||
|
DebugCounter_Init(&g->pending_ctr);
|
||||||
|
|
||||||
|
// init debug object
|
||||||
|
DebugObject_Init(&g->d_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BPendingGroup_Free (BPendingGroup *g)
|
||||||
|
{
|
||||||
|
DebugCounter_Free(&g->pending_ctr);
|
||||||
|
ASSERT(BPending__List_IsEmpty(&g->jobs))
|
||||||
|
DebugObject_Free(&g->d_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
int BPendingGroup_HasJobs (BPendingGroup *g)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&g->d_obj);
|
||||||
|
|
||||||
|
return !BPending__List_IsEmpty(&g->jobs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BPendingGroup_ExecuteJob (BPendingGroup *g)
|
||||||
|
{
|
||||||
|
ASSERT(!BPending__List_IsEmpty(&g->jobs))
|
||||||
|
DebugObject_Access(&g->d_obj);
|
||||||
|
|
||||||
|
// get a job
|
||||||
|
BSmallPending *p = BPending__List_First(&g->jobs);
|
||||||
|
ASSERT(!BPending__ListIsRemoved(p))
|
||||||
|
ASSERT(p->pending)
|
||||||
|
|
||||||
|
// remove from jobs list
|
||||||
|
BPending__List_RemoveFirst(&g->jobs);
|
||||||
|
|
||||||
|
// set not pending
|
||||||
|
BPending__ListMarkRemoved(p);
|
||||||
|
#ifndef NDEBUG
|
||||||
|
p->pending = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// execute job
|
||||||
|
p->handler(p->user);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BSmallPending * BPendingGroup_PeekJob (BPendingGroup *g)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&g->d_obj);
|
||||||
|
|
||||||
|
return BPending__List_First(&g->jobs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BSmallPending_Init (BSmallPending *o, BPendingGroup *g, BSmallPending_handler handler, void *user)
|
||||||
|
{
|
||||||
|
// init arguments
|
||||||
|
o->handler = handler;
|
||||||
|
o->user = user;
|
||||||
|
|
||||||
|
// set not pending
|
||||||
|
BPending__ListMarkRemoved(o);
|
||||||
|
#ifndef NDEBUG
|
||||||
|
o->pending = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// increment pending counter
|
||||||
|
DebugCounter_Increment(&g->pending_ctr);
|
||||||
|
|
||||||
|
// init debug object
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BSmallPending_Free (BSmallPending *o, BPendingGroup *g)
|
||||||
|
{
|
||||||
|
DebugCounter_Decrement(&g->pending_ctr);
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
ASSERT(o->pending == !BPending__ListIsRemoved(o))
|
||||||
|
|
||||||
|
// remove from jobs list
|
||||||
|
if (!BPending__ListIsRemoved(o)) {
|
||||||
|
BPending__List_Remove(&g->jobs, o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BSmallPending_SetHandler (BSmallPending *o, BSmallPending_handler handler, void *user)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// set handler
|
||||||
|
o->handler = handler;
|
||||||
|
o->user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BSmallPending_Set (BSmallPending *o, BPendingGroup *g)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(o->pending == !BPending__ListIsRemoved(o))
|
||||||
|
|
||||||
|
// remove from jobs list
|
||||||
|
if (!BPending__ListIsRemoved(o)) {
|
||||||
|
BPending__List_Remove(&g->jobs, o);
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert to jobs list
|
||||||
|
BPending__List_Prepend(&g->jobs, o);
|
||||||
|
|
||||||
|
// set pending
|
||||||
|
#ifndef NDEBUG
|
||||||
|
o->pending = 1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void BSmallPending_Unset (BSmallPending *o, BPendingGroup *g)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(o->pending == !BPending__ListIsRemoved(o))
|
||||||
|
|
||||||
|
if (!BPending__ListIsRemoved(o)) {
|
||||||
|
// remove from jobs list
|
||||||
|
BPending__List_Remove(&g->jobs, o);
|
||||||
|
|
||||||
|
// set not pending
|
||||||
|
BPending__ListMarkRemoved(o);
|
||||||
|
#ifndef NDEBUG
|
||||||
|
o->pending = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int BSmallPending_IsSet (BSmallPending *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(o->pending == !BPending__ListIsRemoved(o))
|
||||||
|
|
||||||
|
return !BPending__ListIsRemoved(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BPending_Init (BPending *o, BPendingGroup *g, BPending_handler handler, void *user)
|
||||||
|
{
|
||||||
|
BSmallPending_Init(&o->base, g, handler, user);
|
||||||
|
o->g = g;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BPending_Free (BPending *o)
|
||||||
|
{
|
||||||
|
BSmallPending_Free(&o->base, o->g);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BPending_Set (BPending *o)
|
||||||
|
{
|
||||||
|
BSmallPending_Set(&o->base, o->g);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BPending_Unset (BPending *o)
|
||||||
|
{
|
||||||
|
BSmallPending_Unset(&o->base, o->g);
|
||||||
|
}
|
||||||
|
|
||||||
|
int BPending_IsSet (BPending *o)
|
||||||
|
{
|
||||||
|
return BSmallPending_IsSet(&o->base);
|
||||||
|
}
|
|
@ -0,0 +1,250 @@
|
||||||
|
/**
|
||||||
|
* @file BPending.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @section DESCRIPTION
|
||||||
|
*
|
||||||
|
* Module for managing a queue of jobs pending execution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_BPENDING_H
|
||||||
|
#define BADVPN_BPENDING_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <misc/debugcounter.h>
|
||||||
|
#include <structure/SLinkedList.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
|
||||||
|
struct BSmallPending_s;
|
||||||
|
|
||||||
|
#include "BPending_list.h"
|
||||||
|
#include <structure/SLinkedList_decl.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Job execution handler.
|
||||||
|
* It is guaranteed that the associated {@link BSmallPending} object was
|
||||||
|
* in set state.
|
||||||
|
* The {@link BSmallPending} object enters not set state before the handler
|
||||||
|
* is called.
|
||||||
|
*
|
||||||
|
* @param user as in {@link BSmallPending_Init}
|
||||||
|
*/
|
||||||
|
typedef void (*BSmallPending_handler) (void *user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Job execution handler.
|
||||||
|
* It is guaranteed that the associated {@link BPending} object was
|
||||||
|
* in set state.
|
||||||
|
* The {@link BPending} object enters not set state before the handler
|
||||||
|
* is called.
|
||||||
|
*
|
||||||
|
* @param user as in {@link BPending_Init}
|
||||||
|
*/
|
||||||
|
typedef void (*BPending_handler) (void *user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object that contains a list of jobs pending execution.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
BPending__List jobs;
|
||||||
|
DebugCounter pending_ctr;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} BPendingGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object for queuing a job for execution.
|
||||||
|
*/
|
||||||
|
typedef struct BSmallPending_s {
|
||||||
|
BPending_handler handler;
|
||||||
|
void *user;
|
||||||
|
BPending__ListNode pending_node; // optimization: if not pending, .next is this
|
||||||
|
#ifndef NDEBUG
|
||||||
|
uint8_t pending;
|
||||||
|
#endif
|
||||||
|
DebugObject d_obj;
|
||||||
|
} BSmallPending;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object for queuing a job for execution. This is a convenience wrapper
|
||||||
|
* around {@link BSmallPending} with an extra field to remember the
|
||||||
|
* {@link BPendingGroup} being used.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
BSmallPending base;
|
||||||
|
BPendingGroup *g;
|
||||||
|
} BPending;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the object.
|
||||||
|
*
|
||||||
|
* @param g the object
|
||||||
|
*/
|
||||||
|
void BPendingGroup_Init (BPendingGroup *g);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the object.
|
||||||
|
* There must be no {@link BPending} or {@link BSmallPending} objects using
|
||||||
|
* this group.
|
||||||
|
*
|
||||||
|
* @param g the object
|
||||||
|
*/
|
||||||
|
void BPendingGroup_Free (BPendingGroup *g);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if there is at least one job in the queue.
|
||||||
|
*
|
||||||
|
* @param g the object
|
||||||
|
* @return 1 if there is at least one job, 0 if not
|
||||||
|
*/
|
||||||
|
int BPendingGroup_HasJobs (BPendingGroup *g);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the top job on the job list.
|
||||||
|
* The job is removed from the list and enters
|
||||||
|
* not set state before being executed.
|
||||||
|
* There must be at least one job in job list.
|
||||||
|
*
|
||||||
|
* @param g the object
|
||||||
|
*/
|
||||||
|
void BPendingGroup_ExecuteJob (BPendingGroup *g);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the top job on the job list, or NULL if there are none.
|
||||||
|
*
|
||||||
|
* @param g the object
|
||||||
|
* @return the top job if there is at least one job, NULL if not
|
||||||
|
*/
|
||||||
|
BSmallPending * BPendingGroup_PeekJob (BPendingGroup *g);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the object.
|
||||||
|
* The object is initialized in not set state.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param g pending group to use
|
||||||
|
* @param handler job execution handler
|
||||||
|
* @param user value to pass to handler
|
||||||
|
*/
|
||||||
|
void BSmallPending_Init (BSmallPending *o, BPendingGroup *g, BSmallPending_handler handler, void *user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the object.
|
||||||
|
* The execution handler will not be called after the object
|
||||||
|
* is freed.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param g pending group. Must be the same as was used in {@link BSmallPending_Init}.
|
||||||
|
*/
|
||||||
|
void BSmallPending_Free (BSmallPending *o, BPendingGroup *g);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the job execution handler.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param handler job execution handler
|
||||||
|
* @param user value to pass to handler
|
||||||
|
*/
|
||||||
|
void BSmallPending_SetHandler (BSmallPending *o, BSmallPending_handler handler, void *user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables the job, pushing it to the top of the job list.
|
||||||
|
* If the object was already in set state, the job is removed from its
|
||||||
|
* current position in the list before being pushed.
|
||||||
|
* The object enters set state.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param g pending group. Must be the same as was used in {@link BSmallPending_Init}.
|
||||||
|
*/
|
||||||
|
void BSmallPending_Set (BSmallPending *o, BPendingGroup *g);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables the job, removing it from the job list.
|
||||||
|
* If the object was not in set state, nothing is done.
|
||||||
|
* The object enters not set state.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param g pending group. Must be the same as was used in {@link BSmallPending_Init}.
|
||||||
|
*/
|
||||||
|
void BSmallPending_Unset (BSmallPending *o, BPendingGroup *g);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the job is in set state.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @return 1 if in set state, 0 if not
|
||||||
|
*/
|
||||||
|
int BSmallPending_IsSet (BSmallPending *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the object.
|
||||||
|
* The object is initialized in not set state.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param g pending group to use
|
||||||
|
* @param handler job execution handler
|
||||||
|
* @param user value to pass to handler
|
||||||
|
*/
|
||||||
|
void BPending_Init (BPending *o, BPendingGroup *g, BPending_handler handler, void *user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the object.
|
||||||
|
* The execution handler will not be called after the object
|
||||||
|
* is freed.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void BPending_Free (BPending *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables the job, pushing it to the top of the job list.
|
||||||
|
* If the object was already in set state, the job is removed from its
|
||||||
|
* current position in the list before being pushed.
|
||||||
|
* The object enters set state.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void BPending_Set (BPending *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables the job, removing it from the job list.
|
||||||
|
* If the object was not in set state, nothing is done.
|
||||||
|
* The object enters not set state.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void BPending_Unset (BPending *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the job is in set state.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @return 1 if in set state, 0 if not
|
||||||
|
*/
|
||||||
|
int BPending_IsSet (BPending *o);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,4 @@
|
||||||
|
#define SLINKEDLIST_PARAM_NAME BPending__List
|
||||||
|
#define SLINKEDLIST_PARAM_FEATURE_LAST 0
|
||||||
|
#define SLINKEDLIST_PARAM_TYPE_ENTRY struct BSmallPending_s
|
||||||
|
#define SLINKEDLIST_PARAM_MEMBER_NODE pending_node
|
|
@ -0,0 +1,13 @@
|
||||||
|
set(BASE_ADDITIONAL_SOURCES)
|
||||||
|
|
||||||
|
if (HAVE_SYSLOG_H)
|
||||||
|
list(APPEND BASE_ADDITIONAL_SOURCES BLog_syslog.c)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(BASE_SOURCES
|
||||||
|
DebugObject.c
|
||||||
|
BLog.c
|
||||||
|
BPending.c
|
||||||
|
${BASE_ADDITIONAL_SOURCES}
|
||||||
|
)
|
||||||
|
badvpn_add_library(base "" "" "${BASE_SOURCES}")
|
|
@ -0,0 +1,39 @@
|
||||||
|
/**
|
||||||
|
* @file DebugObject.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "DebugObject.h"
|
||||||
|
|
||||||
|
#ifndef BADVPN_PLUGIN
|
||||||
|
#ifndef NDEBUG
|
||||||
|
DebugCounter debugobject_counter = DEBUGCOUNTER_STATIC;
|
||||||
|
#if BADVPN_THREAD_SAFE
|
||||||
|
pthread_mutex_t debugobject_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
|
@ -0,0 +1,147 @@
|
||||||
|
/**
|
||||||
|
* @file DebugObject.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @section DESCRIPTION
|
||||||
|
*
|
||||||
|
* Object used for detecting leaks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_DEBUGOBJECT_H
|
||||||
|
#define BADVPN_DEBUGOBJECT_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#if !defined(BADVPN_THREAD_SAFE) || (BADVPN_THREAD_SAFE != 0 && BADVPN_THREAD_SAFE != 1)
|
||||||
|
#error BADVPN_THREAD_SAFE is not defined or incorrect
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if BADVPN_THREAD_SAFE
|
||||||
|
#include <pthread.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <misc/debugcounter.h>
|
||||||
|
|
||||||
|
#define DEBUGOBJECT_VALID UINT32_C(0x31415926)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object used for detecting leaks.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
#ifndef NDEBUG
|
||||||
|
uint32_t c;
|
||||||
|
#endif
|
||||||
|
} DebugObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the object.
|
||||||
|
*
|
||||||
|
* @param obj the object
|
||||||
|
*/
|
||||||
|
static void DebugObject_Init (DebugObject *obj);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the object.
|
||||||
|
*
|
||||||
|
* @param obj the object
|
||||||
|
*/
|
||||||
|
static void DebugObject_Free (DebugObject *obj);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does nothing.
|
||||||
|
*
|
||||||
|
* @param obj the object
|
||||||
|
*/
|
||||||
|
static void DebugObject_Access (const DebugObject *obj);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does nothing.
|
||||||
|
* There must be no {@link DebugObject}'s initialized.
|
||||||
|
*/
|
||||||
|
static void DebugObjectGlobal_Finish (void);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
extern DebugCounter debugobject_counter;
|
||||||
|
#if BADVPN_THREAD_SAFE
|
||||||
|
extern pthread_mutex_t debugobject_mutex;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void DebugObject_Init (DebugObject *obj)
|
||||||
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
|
||||||
|
obj->c = DEBUGOBJECT_VALID;
|
||||||
|
|
||||||
|
#if BADVPN_THREAD_SAFE
|
||||||
|
ASSERT_FORCE(pthread_mutex_lock(&debugobject_mutex) == 0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DebugCounter_Increment(&debugobject_counter);
|
||||||
|
|
||||||
|
#if BADVPN_THREAD_SAFE
|
||||||
|
ASSERT_FORCE(pthread_mutex_unlock(&debugobject_mutex) == 0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugObject_Free (DebugObject *obj)
|
||||||
|
{
|
||||||
|
ASSERT(obj->c == DEBUGOBJECT_VALID)
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
|
||||||
|
obj->c = 0;
|
||||||
|
|
||||||
|
#if BADVPN_THREAD_SAFE
|
||||||
|
ASSERT_FORCE(pthread_mutex_lock(&debugobject_mutex) == 0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DebugCounter_Decrement(&debugobject_counter);
|
||||||
|
|
||||||
|
#if BADVPN_THREAD_SAFE
|
||||||
|
ASSERT_FORCE(pthread_mutex_unlock(&debugobject_mutex) == 0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugObject_Access (const DebugObject *obj)
|
||||||
|
{
|
||||||
|
ASSERT(obj->c == DEBUGOBJECT_VALID)
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugObjectGlobal_Finish (void)
|
||||||
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
DebugCounter_Free(&debugobject_counter);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,145 @@
|
||||||
|
server 4
|
||||||
|
client 4
|
||||||
|
flooder 4
|
||||||
|
tun2socks 4
|
||||||
|
ncd 4
|
||||||
|
ncd_var 4
|
||||||
|
ncd_list 4
|
||||||
|
ncd_depend 4
|
||||||
|
ncd_multidepend 4
|
||||||
|
ncd_dynamic_depend 4
|
||||||
|
ncd_concat 4
|
||||||
|
ncd_if 4
|
||||||
|
ncd_strcmp 4
|
||||||
|
ncd_regex_match 4
|
||||||
|
ncd_logical 4
|
||||||
|
ncd_sleep 4
|
||||||
|
ncd_print 4
|
||||||
|
ncd_blocker 4
|
||||||
|
ncd_run 4
|
||||||
|
ncd_runonce 4
|
||||||
|
ncd_daemon 4
|
||||||
|
ncd_spawn 4
|
||||||
|
ncd_imperative 4
|
||||||
|
ncd_ref 4
|
||||||
|
ncd_index 4
|
||||||
|
ncd_alias 4
|
||||||
|
ncd_process_manager 4
|
||||||
|
ncd_ondemand 4
|
||||||
|
ncd_foreach 4
|
||||||
|
ncd_choose 4
|
||||||
|
ncd_net_backend_waitdevice 4
|
||||||
|
ncd_net_backend_waitlink 4
|
||||||
|
ncd_net_backend_badvpn 4
|
||||||
|
ncd_net_backend_wpa_supplicant 4
|
||||||
|
ncd_net_backend_rfkill 4
|
||||||
|
ncd_net_up 4
|
||||||
|
ncd_net_dns 4
|
||||||
|
ncd_net_iptables 4
|
||||||
|
ncd_net_ipv4_addr 4
|
||||||
|
ncd_net_ipv4_route 4
|
||||||
|
ncd_net_ipv4_dhcp 4
|
||||||
|
ncd_net_ipv4_arp_probe 4
|
||||||
|
ncd_net_watch_interfaces 4
|
||||||
|
ncd_sys_watch_input 4
|
||||||
|
ncd_sys_watch_usb 4
|
||||||
|
ncd_sys_evdev 4
|
||||||
|
ncd_sys_watch_directory 4
|
||||||
|
StreamPeerIO 4
|
||||||
|
DatagramPeerIO 4
|
||||||
|
BReactor 3
|
||||||
|
BSignal 3
|
||||||
|
FragmentProtoAssembler 4
|
||||||
|
BPredicate 3
|
||||||
|
ServerConnection 4
|
||||||
|
Listener 4
|
||||||
|
DataProto 4
|
||||||
|
FrameDecider 4
|
||||||
|
BSocksClient 4
|
||||||
|
BDHCPClientCore 4
|
||||||
|
BDHCPClient 4
|
||||||
|
NCDIfConfig 4
|
||||||
|
BUnixSignal 4
|
||||||
|
BProcess 4
|
||||||
|
PRStreamSink 4
|
||||||
|
PRStreamSource 4
|
||||||
|
PacketProtoDecoder 4
|
||||||
|
DPRelay 4
|
||||||
|
BThreadWork 4
|
||||||
|
DPReceive 4
|
||||||
|
BInputProcess 4
|
||||||
|
NCDUdevMonitorParser 4
|
||||||
|
NCDUdevMonitor 4
|
||||||
|
NCDUdevCache 4
|
||||||
|
NCDUdevManager 4
|
||||||
|
BTime 4
|
||||||
|
BEncryption 4
|
||||||
|
SPProtoDecoder 4
|
||||||
|
LineBuffer 4
|
||||||
|
BTap 4
|
||||||
|
lwip 4
|
||||||
|
NCDConfigTokenizer 4
|
||||||
|
NCDConfigParser 4
|
||||||
|
NCDValParser 4
|
||||||
|
nsskey 4
|
||||||
|
addr 4
|
||||||
|
PasswordListener 4
|
||||||
|
NCDInterfaceMonitor 4
|
||||||
|
NCDRfkillMonitor 4
|
||||||
|
udpgw 4
|
||||||
|
UdpGwClient 4
|
||||||
|
SocksUdpGwClient 4
|
||||||
|
BNetwork 4
|
||||||
|
BConnection 4
|
||||||
|
BSSLConnection 4
|
||||||
|
BDatagram 4
|
||||||
|
PeerChat 4
|
||||||
|
BArpProbe 4
|
||||||
|
NCDModuleIndex 4
|
||||||
|
NCDModuleProcess 4
|
||||||
|
NCDValGenerator 4
|
||||||
|
ncd_from_string 4
|
||||||
|
ncd_to_string 4
|
||||||
|
ncd_value 4
|
||||||
|
ncd_try 4
|
||||||
|
ncd_sys_request_server 4
|
||||||
|
NCDRequest 4
|
||||||
|
ncd_net_ipv6_wait_dynamic_addr 4
|
||||||
|
NCDRequestClient 4
|
||||||
|
ncd_request 4
|
||||||
|
ncd_sys_request_client 4
|
||||||
|
ncd_exit 4
|
||||||
|
ncd_getargs 4
|
||||||
|
ncd_arithmetic 4
|
||||||
|
ncd_parse 4
|
||||||
|
ncd_valuemetic 4
|
||||||
|
ncd_file 4
|
||||||
|
ncd_netmask 4
|
||||||
|
ncd_implode 4
|
||||||
|
ncd_call2 4
|
||||||
|
ncd_assert 4
|
||||||
|
ncd_reboot 4
|
||||||
|
ncd_explode 4
|
||||||
|
NCDPlaceholderDb 4
|
||||||
|
NCDVal 4
|
||||||
|
ncd_net_ipv6_addr 4
|
||||||
|
ncd_net_ipv6_route 4
|
||||||
|
ncd_net_ipv4_addr_in_network 4
|
||||||
|
ncd_net_ipv6_addr_in_network 4
|
||||||
|
dostest_server 4
|
||||||
|
dostest_attacker 4
|
||||||
|
ncd_timer 4
|
||||||
|
ncd_file_open 4
|
||||||
|
ncd_backtrack 4
|
||||||
|
ncd_socket 4
|
||||||
|
ncd_depend_scope 4
|
||||||
|
ncd_substr 4
|
||||||
|
ncd_sys_start_process 4
|
||||||
|
NCDBuildProgram 4
|
||||||
|
ncd_log 4
|
||||||
|
ncd_log_msg 4
|
||||||
|
ncd_buffer 4
|
||||||
|
ncd_getenv 4
|
||||||
|
BThreadSignal 4
|
||||||
|
BLockReactor 4
|
||||||
|
ncd_load_module 4
|
|
@ -0,0 +1,121 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once "blog_functions.php";
|
||||||
|
|
||||||
|
function assert_failure ($script, $line, $message)
|
||||||
|
{
|
||||||
|
if ($message == "") {
|
||||||
|
fatal_error("Assertion failure at {$script}:{$line}");
|
||||||
|
} else {
|
||||||
|
fatal_error("Assertion failure at {$script}:{$line}: {$message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_options(ASSERT_CALLBACK, "assert_failure");
|
||||||
|
|
||||||
|
function print_help ($name)
|
||||||
|
{
|
||||||
|
echo <<<EOD
|
||||||
|
Usage: {$name}
|
||||||
|
--input-file <file> Input channels file.
|
||||||
|
--output-dir <dir> Destination directory for generated files.
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
$input_file = "";
|
||||||
|
$output_dir = "";
|
||||||
|
|
||||||
|
for ($i = 1; $i < $argc;) {
|
||||||
|
$arg = $argv[$i++];
|
||||||
|
switch ($arg) {
|
||||||
|
case "--input-file":
|
||||||
|
$input_file = $argv[$i++];
|
||||||
|
break;
|
||||||
|
case "--output-dir":
|
||||||
|
$output_dir = $argv[$i++];
|
||||||
|
break;
|
||||||
|
case "--help":
|
||||||
|
print_help($argv[0]);
|
||||||
|
exit(0);
|
||||||
|
default:
|
||||||
|
fatal_error("Unknown option: {$arg}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($input_file == "") {
|
||||||
|
fatal_error("--input-file missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($output_dir == "") {
|
||||||
|
fatal_error("--output-dir missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($data = file_get_contents($input_file)) === FALSE) {
|
||||||
|
fatal_error("Failed to read input file");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tokenize($data, $tokens)) {
|
||||||
|
fatal_error("Failed to tokenize");
|
||||||
|
}
|
||||||
|
|
||||||
|
$i = 0;
|
||||||
|
$channels_defines = "";
|
||||||
|
$channels_list = "";
|
||||||
|
|
||||||
|
reset($tokens);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (($ch_name = current($tokens)) === FALSE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
next($tokens);
|
||||||
|
if (($ch_priority = current($tokens)) === FALSE) {
|
||||||
|
fatal_error("missing priority");
|
||||||
|
}
|
||||||
|
next($tokens);
|
||||||
|
if ($ch_name[0] != "name") {
|
||||||
|
fatal_error("name is not a name");
|
||||||
|
}
|
||||||
|
if ($ch_priority[0] != "number") {
|
||||||
|
fatal_error("priority is not a number");
|
||||||
|
}
|
||||||
|
|
||||||
|
$channel_file = <<<EOD
|
||||||
|
#ifdef BLOG_CURRENT_CHANNEL
|
||||||
|
#undef BLOG_CURRENT_CHANNEL
|
||||||
|
#endif
|
||||||
|
#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_{$ch_name[1]}
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
$channels_defines .= <<<EOD
|
||||||
|
#define BLOG_CHANNEL_{$ch_name[1]} {$i}
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
$channels_list .= <<<EOD
|
||||||
|
{"{$ch_name[1]}", {$ch_priority[1]}},
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
if (file_put_contents("{$output_dir}/blog_channel_{$ch_name[1]}.h", $channel_file) === NULL) {
|
||||||
|
fatal_error("{$input_file}: Failed to write channel file");
|
||||||
|
}
|
||||||
|
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$channels_defines .= <<<EOD
|
||||||
|
#define BLOG_NUM_CHANNELS {$i}
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
if (file_put_contents("{$output_dir}/blog_channels_defines.h", $channels_defines) === NULL) {
|
||||||
|
fatal_error("{$input_file}: Failed to write channels defines file");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_put_contents("{$output_dir}/blog_channels_list.h", $channels_list) === NULL) {
|
||||||
|
fatal_error("{$input_file}: Failed to write channels list file");
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
function tokenize ($str, &$out) {
|
||||||
|
$out = array();
|
||||||
|
|
||||||
|
while (strlen($str) > 0) {
|
||||||
|
if (preg_match('/^\\/\\/.*/', $str, $matches)) {
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else if (preg_match('/^\\s+/', $str, $matches)) {
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else if (preg_match('/^[0-9]+/', $str, $matches)) {
|
||||||
|
$out[] = array('number', $matches[0]);
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else if (preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*/', $str, $matches)) {
|
||||||
|
$out[] = array('name', $matches[0]);
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fatal_error ($message)
|
||||||
|
{
|
||||||
|
fwrite(STDERR, "Fatal error: $message\n");
|
||||||
|
|
||||||
|
ob_get_clean();
|
||||||
|
exit(1);
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
/**
|
||||||
|
* @file BProto.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @section DESCRIPTION
|
||||||
|
*
|
||||||
|
* Definitions for BProto serialization.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_BPROTO_BPROTO_H
|
||||||
|
#define BADVPN_BPROTO_BPROTO_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <misc/packed.h>
|
||||||
|
|
||||||
|
#define BPROTO_TYPE_UINT8 1
|
||||||
|
#define BPROTO_TYPE_UINT16 2
|
||||||
|
#define BPROTO_TYPE_UINT32 3
|
||||||
|
#define BPROTO_TYPE_UINT64 4
|
||||||
|
#define BPROTO_TYPE_DATA 5
|
||||||
|
#define BPROTO_TYPE_CONSTDATA 6
|
||||||
|
|
||||||
|
B_START_PACKED
|
||||||
|
struct BProto_header_s {
|
||||||
|
uint16_t id;
|
||||||
|
uint16_t type;
|
||||||
|
} B_PACKED;
|
||||||
|
B_END_PACKED
|
||||||
|
|
||||||
|
B_START_PACKED
|
||||||
|
struct BProto_uint8_s {
|
||||||
|
uint8_t v;
|
||||||
|
} B_PACKED;
|
||||||
|
B_END_PACKED
|
||||||
|
|
||||||
|
B_START_PACKED
|
||||||
|
struct BProto_uint16_s {
|
||||||
|
uint16_t v;
|
||||||
|
} B_PACKED;
|
||||||
|
B_END_PACKED
|
||||||
|
|
||||||
|
B_START_PACKED
|
||||||
|
struct BProto_uint32_s {
|
||||||
|
uint32_t v;
|
||||||
|
} B_PACKED;
|
||||||
|
B_END_PACKED
|
||||||
|
|
||||||
|
B_START_PACKED
|
||||||
|
struct BProto_uint64_s {
|
||||||
|
uint64_t v;
|
||||||
|
} B_PACKED;
|
||||||
|
B_END_PACKED
|
||||||
|
|
||||||
|
B_START_PACKED
|
||||||
|
struct BProto_data_header_s {
|
||||||
|
uint32_t len;
|
||||||
|
} B_PACKED;
|
||||||
|
B_END_PACKED
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,99 @@
|
||||||
|
%class ProtoParser
|
||||||
|
%start file
|
||||||
|
|
||||||
|
file =
|
||||||
|
directives messages {
|
||||||
|
$$ = array(
|
||||||
|
"directives" => $1,
|
||||||
|
"messages" => $2
|
||||||
|
);
|
||||||
|
}.
|
||||||
|
|
||||||
|
directives =
|
||||||
|
{
|
||||||
|
$$ = array();
|
||||||
|
} |
|
||||||
|
directive semicolon directives {
|
||||||
|
$$ = array_merge(array($1), $3);
|
||||||
|
}.
|
||||||
|
|
||||||
|
directive =
|
||||||
|
include string {
|
||||||
|
$$ = array(
|
||||||
|
"type" => "include",
|
||||||
|
"file" => $2
|
||||||
|
);
|
||||||
|
}.
|
||||||
|
|
||||||
|
messages =
|
||||||
|
msgspec {
|
||||||
|
$$ = array($1);
|
||||||
|
} |
|
||||||
|
msgspec messages {
|
||||||
|
$$ = array_merge(array($1), $2);
|
||||||
|
}.
|
||||||
|
|
||||||
|
msgspec =
|
||||||
|
message name spar entries epar semicolon {
|
||||||
|
$$ = array(
|
||||||
|
"name" => $2,
|
||||||
|
"entries" => $4
|
||||||
|
);
|
||||||
|
}.
|
||||||
|
|
||||||
|
entries =
|
||||||
|
entry {
|
||||||
|
$$ = array($1);
|
||||||
|
} |
|
||||||
|
entry entries {
|
||||||
|
$$ = array_merge(array($1), $2);
|
||||||
|
}.
|
||||||
|
|
||||||
|
entry =
|
||||||
|
cardinality type name equals number semicolon {
|
||||||
|
$$ = array(
|
||||||
|
"cardinality" => $1,
|
||||||
|
"type" => $2,
|
||||||
|
"name" => $3,
|
||||||
|
"id" => $5
|
||||||
|
);
|
||||||
|
}.
|
||||||
|
|
||||||
|
cardinality =
|
||||||
|
repeated {
|
||||||
|
$$ = "repeated";
|
||||||
|
} |
|
||||||
|
optional {
|
||||||
|
$$ = "optional";
|
||||||
|
} |
|
||||||
|
required {
|
||||||
|
$$ = "required";
|
||||||
|
} |
|
||||||
|
required repeated {
|
||||||
|
$$ = "required repeated";
|
||||||
|
}.
|
||||||
|
|
||||||
|
type =
|
||||||
|
uint {
|
||||||
|
$$ = array(
|
||||||
|
"type" => "uint",
|
||||||
|
"size" => $1
|
||||||
|
);
|
||||||
|
} |
|
||||||
|
data {
|
||||||
|
$$ = array(
|
||||||
|
"type" => "data"
|
||||||
|
);
|
||||||
|
} |
|
||||||
|
data srpar string erpar {
|
||||||
|
$$ = array(
|
||||||
|
"type" => "constdata",
|
||||||
|
"size" => $3
|
||||||
|
);
|
||||||
|
} |
|
||||||
|
message name {
|
||||||
|
$$ = array(
|
||||||
|
"type" => "message",
|
||||||
|
"message" => $2
|
||||||
|
);
|
||||||
|
}.
|
|
@ -0,0 +1,560 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
DON'T EDIT THIS FILE!
|
||||||
|
|
||||||
|
This file was automatically generated by the Lime parser generator.
|
||||||
|
The real source code you should be looking at is in one or more
|
||||||
|
grammar files in the Lime format.
|
||||||
|
|
||||||
|
THE ONLY REASON TO LOOK AT THIS FILE is to see where in the grammar
|
||||||
|
file that your error happened, because there are enough comments to
|
||||||
|
help you debug your grammar.
|
||||||
|
|
||||||
|
If you ignore this warning, you're shooting yourself in the brain,
|
||||||
|
not the foot.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ProtoParser extends lime_parser {
|
||||||
|
var $qi = 0;
|
||||||
|
var $i = array (
|
||||||
|
0 =>
|
||||||
|
array (
|
||||||
|
'directives' => 's 1',
|
||||||
|
'directive' => 's 30',
|
||||||
|
'include' => 's 33',
|
||||||
|
'file' => 's 35',
|
||||||
|
'\'start\'' => 'a \'start\'',
|
||||||
|
'message' => 'r 1',
|
||||||
|
),
|
||||||
|
1 =>
|
||||||
|
array (
|
||||||
|
'messages' => 's 2',
|
||||||
|
'msgspec' => 's 3',
|
||||||
|
'message' => 's 5',
|
||||||
|
),
|
||||||
|
2 =>
|
||||||
|
array (
|
||||||
|
'#' => 'r 0',
|
||||||
|
),
|
||||||
|
3 =>
|
||||||
|
array (
|
||||||
|
'msgspec' => 's 3',
|
||||||
|
'messages' => 's 4',
|
||||||
|
'message' => 's 5',
|
||||||
|
'#' => 'r 4',
|
||||||
|
),
|
||||||
|
4 =>
|
||||||
|
array (
|
||||||
|
'#' => 'r 5',
|
||||||
|
),
|
||||||
|
5 =>
|
||||||
|
array (
|
||||||
|
'name' => 's 6',
|
||||||
|
),
|
||||||
|
6 =>
|
||||||
|
array (
|
||||||
|
'spar' => 's 7',
|
||||||
|
),
|
||||||
|
7 =>
|
||||||
|
array (
|
||||||
|
'entries' => 's 8',
|
||||||
|
'entry' => 's 11',
|
||||||
|
'cardinality' => 's 13',
|
||||||
|
'repeated' => 's 26',
|
||||||
|
'optional' => 's 27',
|
||||||
|
'required' => 's 28',
|
||||||
|
),
|
||||||
|
8 =>
|
||||||
|
array (
|
||||||
|
'epar' => 's 9',
|
||||||
|
),
|
||||||
|
9 =>
|
||||||
|
array (
|
||||||
|
'semicolon' => 's 10',
|
||||||
|
),
|
||||||
|
10 =>
|
||||||
|
array (
|
||||||
|
'message' => 'r 6',
|
||||||
|
'#' => 'r 6',
|
||||||
|
),
|
||||||
|
11 =>
|
||||||
|
array (
|
||||||
|
'entry' => 's 11',
|
||||||
|
'entries' => 's 12',
|
||||||
|
'cardinality' => 's 13',
|
||||||
|
'repeated' => 's 26',
|
||||||
|
'optional' => 's 27',
|
||||||
|
'required' => 's 28',
|
||||||
|
'epar' => 'r 7',
|
||||||
|
),
|
||||||
|
12 =>
|
||||||
|
array (
|
||||||
|
'epar' => 'r 8',
|
||||||
|
),
|
||||||
|
13 =>
|
||||||
|
array (
|
||||||
|
'type' => 's 14',
|
||||||
|
'uint' => 's 19',
|
||||||
|
'data' => 's 20',
|
||||||
|
'message' => 's 24',
|
||||||
|
),
|
||||||
|
14 =>
|
||||||
|
array (
|
||||||
|
'name' => 's 15',
|
||||||
|
),
|
||||||
|
15 =>
|
||||||
|
array (
|
||||||
|
'equals' => 's 16',
|
||||||
|
),
|
||||||
|
16 =>
|
||||||
|
array (
|
||||||
|
'number' => 's 17',
|
||||||
|
),
|
||||||
|
17 =>
|
||||||
|
array (
|
||||||
|
'semicolon' => 's 18',
|
||||||
|
),
|
||||||
|
18 =>
|
||||||
|
array (
|
||||||
|
'repeated' => 'r 9',
|
||||||
|
'optional' => 'r 9',
|
||||||
|
'required' => 'r 9',
|
||||||
|
'epar' => 'r 9',
|
||||||
|
),
|
||||||
|
19 =>
|
||||||
|
array (
|
||||||
|
'name' => 'r 14',
|
||||||
|
),
|
||||||
|
20 =>
|
||||||
|
array (
|
||||||
|
'srpar' => 's 21',
|
||||||
|
'name' => 'r 15',
|
||||||
|
),
|
||||||
|
21 =>
|
||||||
|
array (
|
||||||
|
'string' => 's 22',
|
||||||
|
),
|
||||||
|
22 =>
|
||||||
|
array (
|
||||||
|
'erpar' => 's 23',
|
||||||
|
),
|
||||||
|
23 =>
|
||||||
|
array (
|
||||||
|
'name' => 'r 16',
|
||||||
|
),
|
||||||
|
24 =>
|
||||||
|
array (
|
||||||
|
'name' => 's 25',
|
||||||
|
),
|
||||||
|
25 =>
|
||||||
|
array (
|
||||||
|
'name' => 'r 17',
|
||||||
|
),
|
||||||
|
26 =>
|
||||||
|
array (
|
||||||
|
'uint' => 'r 10',
|
||||||
|
'data' => 'r 10',
|
||||||
|
'message' => 'r 10',
|
||||||
|
),
|
||||||
|
27 =>
|
||||||
|
array (
|
||||||
|
'uint' => 'r 11',
|
||||||
|
'data' => 'r 11',
|
||||||
|
'message' => 'r 11',
|
||||||
|
),
|
||||||
|
28 =>
|
||||||
|
array (
|
||||||
|
'repeated' => 's 29',
|
||||||
|
'uint' => 'r 12',
|
||||||
|
'data' => 'r 12',
|
||||||
|
'message' => 'r 12',
|
||||||
|
),
|
||||||
|
29 =>
|
||||||
|
array (
|
||||||
|
'uint' => 'r 13',
|
||||||
|
'data' => 'r 13',
|
||||||
|
'message' => 'r 13',
|
||||||
|
),
|
||||||
|
30 =>
|
||||||
|
array (
|
||||||
|
'semicolon' => 's 31',
|
||||||
|
),
|
||||||
|
31 =>
|
||||||
|
array (
|
||||||
|
'directive' => 's 30',
|
||||||
|
'directives' => 's 32',
|
||||||
|
'include' => 's 33',
|
||||||
|
'message' => 'r 1',
|
||||||
|
),
|
||||||
|
32 =>
|
||||||
|
array (
|
||||||
|
'message' => 'r 2',
|
||||||
|
),
|
||||||
|
33 =>
|
||||||
|
array (
|
||||||
|
'string' => 's 34',
|
||||||
|
),
|
||||||
|
34 =>
|
||||||
|
array (
|
||||||
|
'semicolon' => 'r 3',
|
||||||
|
),
|
||||||
|
35 =>
|
||||||
|
array (
|
||||||
|
'#' => 'r 18',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
function reduce_0_file_1($tokens, &$result) {
|
||||||
|
#
|
||||||
|
# (0) file := directives messages
|
||||||
|
#
|
||||||
|
$result = reset($tokens);
|
||||||
|
|
||||||
|
$result = array(
|
||||||
|
"directives" => $tokens[0],
|
||||||
|
"messages" => $tokens[1]
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function reduce_1_directives_1($tokens, &$result) {
|
||||||
|
#
|
||||||
|
# (1) directives :=
|
||||||
|
#
|
||||||
|
$result = reset($tokens);
|
||||||
|
|
||||||
|
$result = array();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function reduce_2_directives_2($tokens, &$result) {
|
||||||
|
#
|
||||||
|
# (2) directives := directive semicolon directives
|
||||||
|
#
|
||||||
|
$result = reset($tokens);
|
||||||
|
|
||||||
|
$result = array_merge(array($tokens[0]), $tokens[2]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function reduce_3_directive_1($tokens, &$result) {
|
||||||
|
#
|
||||||
|
# (3) directive := include string
|
||||||
|
#
|
||||||
|
$result = reset($tokens);
|
||||||
|
|
||||||
|
$result = array(
|
||||||
|
"type" => "include",
|
||||||
|
"file" => $tokens[1]
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function reduce_4_messages_1($tokens, &$result) {
|
||||||
|
#
|
||||||
|
# (4) messages := msgspec
|
||||||
|
#
|
||||||
|
$result = reset($tokens);
|
||||||
|
|
||||||
|
$result = array($tokens[0]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function reduce_5_messages_2($tokens, &$result) {
|
||||||
|
#
|
||||||
|
# (5) messages := msgspec messages
|
||||||
|
#
|
||||||
|
$result = reset($tokens);
|
||||||
|
|
||||||
|
$result = array_merge(array($tokens[0]), $tokens[1]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function reduce_6_msgspec_1($tokens, &$result) {
|
||||||
|
#
|
||||||
|
# (6) msgspec := message name spar entries epar semicolon
|
||||||
|
#
|
||||||
|
$result = reset($tokens);
|
||||||
|
|
||||||
|
$result = array(
|
||||||
|
"name" => $tokens[1],
|
||||||
|
"entries" => $tokens[3]
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function reduce_7_entries_1($tokens, &$result) {
|
||||||
|
#
|
||||||
|
# (7) entries := entry
|
||||||
|
#
|
||||||
|
$result = reset($tokens);
|
||||||
|
|
||||||
|
$result = array($tokens[0]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function reduce_8_entries_2($tokens, &$result) {
|
||||||
|
#
|
||||||
|
# (8) entries := entry entries
|
||||||
|
#
|
||||||
|
$result = reset($tokens);
|
||||||
|
|
||||||
|
$result = array_merge(array($tokens[0]), $tokens[1]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function reduce_9_entry_1($tokens, &$result) {
|
||||||
|
#
|
||||||
|
# (9) entry := cardinality type name equals number semicolon
|
||||||
|
#
|
||||||
|
$result = reset($tokens);
|
||||||
|
|
||||||
|
$result = array(
|
||||||
|
"cardinality" => $tokens[0],
|
||||||
|
"type" => $tokens[1],
|
||||||
|
"name" => $tokens[2],
|
||||||
|
"id" => $tokens[4]
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function reduce_10_cardinality_1($tokens, &$result) {
|
||||||
|
#
|
||||||
|
# (10) cardinality := repeated
|
||||||
|
#
|
||||||
|
$result = reset($tokens);
|
||||||
|
|
||||||
|
$result = "repeated";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function reduce_11_cardinality_2($tokens, &$result) {
|
||||||
|
#
|
||||||
|
# (11) cardinality := optional
|
||||||
|
#
|
||||||
|
$result = reset($tokens);
|
||||||
|
|
||||||
|
$result = "optional";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function reduce_12_cardinality_3($tokens, &$result) {
|
||||||
|
#
|
||||||
|
# (12) cardinality := required
|
||||||
|
#
|
||||||
|
$result = reset($tokens);
|
||||||
|
|
||||||
|
$result = "required";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function reduce_13_cardinality_4($tokens, &$result) {
|
||||||
|
#
|
||||||
|
# (13) cardinality := required repeated
|
||||||
|
#
|
||||||
|
$result = reset($tokens);
|
||||||
|
|
||||||
|
$result = "required repeated";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function reduce_14_type_1($tokens, &$result) {
|
||||||
|
#
|
||||||
|
# (14) type := uint
|
||||||
|
#
|
||||||
|
$result = reset($tokens);
|
||||||
|
|
||||||
|
$result = array(
|
||||||
|
"type" => "uint",
|
||||||
|
"size" => $tokens[0]
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function reduce_15_type_2($tokens, &$result) {
|
||||||
|
#
|
||||||
|
# (15) type := data
|
||||||
|
#
|
||||||
|
$result = reset($tokens);
|
||||||
|
|
||||||
|
$result = array(
|
||||||
|
"type" => "data"
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function reduce_16_type_3($tokens, &$result) {
|
||||||
|
#
|
||||||
|
# (16) type := data srpar string erpar
|
||||||
|
#
|
||||||
|
$result = reset($tokens);
|
||||||
|
|
||||||
|
$result = array(
|
||||||
|
"type" => "constdata",
|
||||||
|
"size" => $tokens[2]
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function reduce_17_type_4($tokens, &$result) {
|
||||||
|
#
|
||||||
|
# (17) type := message name
|
||||||
|
#
|
||||||
|
$result = reset($tokens);
|
||||||
|
|
||||||
|
$result = array(
|
||||||
|
"type" => "message",
|
||||||
|
"message" => $tokens[1]
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function reduce_18_start_1($tokens, &$result) {
|
||||||
|
#
|
||||||
|
# (18) 'start' := file
|
||||||
|
#
|
||||||
|
$result = reset($tokens);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var $method = array (
|
||||||
|
0 => 'reduce_0_file_1',
|
||||||
|
1 => 'reduce_1_directives_1',
|
||||||
|
2 => 'reduce_2_directives_2',
|
||||||
|
3 => 'reduce_3_directive_1',
|
||||||
|
4 => 'reduce_4_messages_1',
|
||||||
|
5 => 'reduce_5_messages_2',
|
||||||
|
6 => 'reduce_6_msgspec_1',
|
||||||
|
7 => 'reduce_7_entries_1',
|
||||||
|
8 => 'reduce_8_entries_2',
|
||||||
|
9 => 'reduce_9_entry_1',
|
||||||
|
10 => 'reduce_10_cardinality_1',
|
||||||
|
11 => 'reduce_11_cardinality_2',
|
||||||
|
12 => 'reduce_12_cardinality_3',
|
||||||
|
13 => 'reduce_13_cardinality_4',
|
||||||
|
14 => 'reduce_14_type_1',
|
||||||
|
15 => 'reduce_15_type_2',
|
||||||
|
16 => 'reduce_16_type_3',
|
||||||
|
17 => 'reduce_17_type_4',
|
||||||
|
18 => 'reduce_18_start_1',
|
||||||
|
);
|
||||||
|
var $a = array (
|
||||||
|
0 =>
|
||||||
|
array (
|
||||||
|
'symbol' => 'file',
|
||||||
|
'len' => 2,
|
||||||
|
'replace' => true,
|
||||||
|
),
|
||||||
|
1 =>
|
||||||
|
array (
|
||||||
|
'symbol' => 'directives',
|
||||||
|
'len' => 0,
|
||||||
|
'replace' => true,
|
||||||
|
),
|
||||||
|
2 =>
|
||||||
|
array (
|
||||||
|
'symbol' => 'directives',
|
||||||
|
'len' => 3,
|
||||||
|
'replace' => true,
|
||||||
|
),
|
||||||
|
3 =>
|
||||||
|
array (
|
||||||
|
'symbol' => 'directive',
|
||||||
|
'len' => 2,
|
||||||
|
'replace' => true,
|
||||||
|
),
|
||||||
|
4 =>
|
||||||
|
array (
|
||||||
|
'symbol' => 'messages',
|
||||||
|
'len' => 1,
|
||||||
|
'replace' => true,
|
||||||
|
),
|
||||||
|
5 =>
|
||||||
|
array (
|
||||||
|
'symbol' => 'messages',
|
||||||
|
'len' => 2,
|
||||||
|
'replace' => true,
|
||||||
|
),
|
||||||
|
6 =>
|
||||||
|
array (
|
||||||
|
'symbol' => 'msgspec',
|
||||||
|
'len' => 6,
|
||||||
|
'replace' => true,
|
||||||
|
),
|
||||||
|
7 =>
|
||||||
|
array (
|
||||||
|
'symbol' => 'entries',
|
||||||
|
'len' => 1,
|
||||||
|
'replace' => true,
|
||||||
|
),
|
||||||
|
8 =>
|
||||||
|
array (
|
||||||
|
'symbol' => 'entries',
|
||||||
|
'len' => 2,
|
||||||
|
'replace' => true,
|
||||||
|
),
|
||||||
|
9 =>
|
||||||
|
array (
|
||||||
|
'symbol' => 'entry',
|
||||||
|
'len' => 6,
|
||||||
|
'replace' => true,
|
||||||
|
),
|
||||||
|
10 =>
|
||||||
|
array (
|
||||||
|
'symbol' => 'cardinality',
|
||||||
|
'len' => 1,
|
||||||
|
'replace' => true,
|
||||||
|
),
|
||||||
|
11 =>
|
||||||
|
array (
|
||||||
|
'symbol' => 'cardinality',
|
||||||
|
'len' => 1,
|
||||||
|
'replace' => true,
|
||||||
|
),
|
||||||
|
12 =>
|
||||||
|
array (
|
||||||
|
'symbol' => 'cardinality',
|
||||||
|
'len' => 1,
|
||||||
|
'replace' => true,
|
||||||
|
),
|
||||||
|
13 =>
|
||||||
|
array (
|
||||||
|
'symbol' => 'cardinality',
|
||||||
|
'len' => 2,
|
||||||
|
'replace' => true,
|
||||||
|
),
|
||||||
|
14 =>
|
||||||
|
array (
|
||||||
|
'symbol' => 'type',
|
||||||
|
'len' => 1,
|
||||||
|
'replace' => true,
|
||||||
|
),
|
||||||
|
15 =>
|
||||||
|
array (
|
||||||
|
'symbol' => 'type',
|
||||||
|
'len' => 1,
|
||||||
|
'replace' => true,
|
||||||
|
),
|
||||||
|
16 =>
|
||||||
|
array (
|
||||||
|
'symbol' => 'type',
|
||||||
|
'len' => 4,
|
||||||
|
'replace' => true,
|
||||||
|
),
|
||||||
|
17 =>
|
||||||
|
array (
|
||||||
|
'symbol' => 'type',
|
||||||
|
'len' => 2,
|
||||||
|
'replace' => true,
|
||||||
|
),
|
||||||
|
18 =>
|
||||||
|
array (
|
||||||
|
'symbol' => '\'start\'',
|
||||||
|
'len' => 1,
|
||||||
|
'replace' => true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @file bproto.php
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once "lime/parse_engine.php";
|
||||||
|
require_once "ProtoParser.php";
|
||||||
|
require_once "bproto_functions.php";
|
||||||
|
|
||||||
|
function assert_failure ($script, $line, $message)
|
||||||
|
{
|
||||||
|
if ($message == "") {
|
||||||
|
fatal_error("Assertion failure at {$script}:{$line}");
|
||||||
|
} else {
|
||||||
|
fatal_error("Assertion failure at {$script}:{$line}: {$message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_options(ASSERT_CALLBACK, "assert_failure");
|
||||||
|
|
||||||
|
function print_help ($name)
|
||||||
|
{
|
||||||
|
echo <<<EOD
|
||||||
|
Usage: {$name}
|
||||||
|
--name <string> Output file prefix.
|
||||||
|
--input-file <file> Message file to generate source for.
|
||||||
|
--output-dir <dir> Destination directory for generated files.
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = "";
|
||||||
|
$input_file = "";
|
||||||
|
$output_dir = "";
|
||||||
|
|
||||||
|
for ($i = 1; $i < $argc;) {
|
||||||
|
$arg = $argv[$i++];
|
||||||
|
switch ($arg) {
|
||||||
|
case "--name":
|
||||||
|
$name = $argv[$i++];
|
||||||
|
break;
|
||||||
|
case "--input-file":
|
||||||
|
$input_file = $argv[$i++];
|
||||||
|
break;
|
||||||
|
case "--output-dir":
|
||||||
|
$output_dir = $argv[$i++];
|
||||||
|
break;
|
||||||
|
case "--help":
|
||||||
|
print_help($argv[0]);
|
||||||
|
exit(0);
|
||||||
|
default:
|
||||||
|
fatal_error("Unknown option: {$arg}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($name == "") {
|
||||||
|
fatal_error("--name missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($input_file == "") {
|
||||||
|
fatal_error("--input-file missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($output_dir == "") {
|
||||||
|
fatal_error("--output-dir missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($data = file_get_contents($input_file)) === FALSE) {
|
||||||
|
fatal_error("Failed to read input file");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tokenize($data, $tokens)) {
|
||||||
|
fatal_error("Failed to tokenize");
|
||||||
|
}
|
||||||
|
|
||||||
|
$parser = new parse_engine(new ProtoParser());
|
||||||
|
|
||||||
|
try {
|
||||||
|
foreach ($tokens as $token) {
|
||||||
|
$parser->eat($token[0], $token[1]);
|
||||||
|
}
|
||||||
|
$parser->eat_eof();
|
||||||
|
} catch (parse_error $e) {
|
||||||
|
fatal_error("$input_file: Parse error: ".$e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = generate_header($name, $parser->semantic["directives"], $parser->semantic["messages"]);
|
||||||
|
if (file_put_contents("{$output_dir}/{$name}.h", $data) === NULL) {
|
||||||
|
fatal_error("{$input_file}: Failed to write .h file");
|
||||||
|
}
|
|
@ -0,0 +1,777 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
function tokenize ($str, &$out) {
|
||||||
|
$out = array();
|
||||||
|
|
||||||
|
while (strlen($str) > 0) {
|
||||||
|
if (preg_match('/^\\/\\/.*/', $str, $matches)) {
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else if (preg_match('/^\\s+/', $str, $matches)) {
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else if (preg_match('/^include/', $str, $matches)) {
|
||||||
|
$out[] = array('include', null);
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else if (preg_match('/^message/', $str, $matches)) {
|
||||||
|
$out[] = array('message', null);
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else if (preg_match('/^repeated/', $str, $matches)) {
|
||||||
|
$out[] = array('repeated', null);
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else if (preg_match('/^required/', $str, $matches)) {
|
||||||
|
$out[] = array('required', null);
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else if (preg_match('/^optional/', $str, $matches)) {
|
||||||
|
$out[] = array('optional', null);
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else if (preg_match('/^{/', $str, $matches)) {
|
||||||
|
$out[] = array('spar', null);
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else if (preg_match('/^}/', $str, $matches)) {
|
||||||
|
$out[] = array('epar', null);
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else if (preg_match('/^\(/', $str, $matches)) {
|
||||||
|
$out[] = array('srpar', null);
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else if (preg_match('/^\)/', $str, $matches)) {
|
||||||
|
$out[] = array('erpar', null);
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else if (preg_match('/^=/', $str, $matches)) {
|
||||||
|
$out[] = array('equals', null);
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else if (preg_match('/^;/', $str, $matches)) {
|
||||||
|
$out[] = array('semicolon', null);
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else if (preg_match('/^uint(8|16|32|64)/', $str, $matches)) {
|
||||||
|
$out[] = array('uint', $matches[1]);
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else if (preg_match('/^data/', $str, $matches)) {
|
||||||
|
$out[] = array('data', null);
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else if (preg_match('/^[0-9]+/', $str, $matches)) {
|
||||||
|
$out[] = array('number', $matches[0]);
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else if (preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*/', $str, $matches)) {
|
||||||
|
$out[] = array('name', $matches[0]);
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else if (preg_match('/^"([^"]*)"/', $str, $matches)) {
|
||||||
|
$out[] = array('string', $matches[1]);
|
||||||
|
$str = substr($str, strlen($matches[0]));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fatal_error ($message)
|
||||||
|
{
|
||||||
|
fwrite(STDERR, "Fatal error: $message\n");
|
||||||
|
|
||||||
|
ob_get_clean();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_writer_decl ($msg, $entry)
|
||||||
|
{
|
||||||
|
switch ($entry["type"]["type"]) {
|
||||||
|
case "uint":
|
||||||
|
return "void {$msg["name"]}Writer_Add{$entry["name"]} ({$msg["name"]}Writer *o, uint{$entry["type"]["size"]}_t v)";
|
||||||
|
case "data":
|
||||||
|
return "uint8_t * {$msg["name"]}Writer_Add{$entry["name"]} ({$msg["name"]}Writer *o, int len)";
|
||||||
|
case "constdata":
|
||||||
|
return "uint8_t * {$msg["name"]}Writer_Add{$entry["name"]} ({$msg["name"]}Writer *o)";
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_parser_decl ($msg, $entry)
|
||||||
|
{
|
||||||
|
switch ($entry["type"]["type"]) {
|
||||||
|
case "uint":
|
||||||
|
return "int {$msg["name"]}Parser_Get{$entry["name"]} ({$msg["name"]}Parser *o, uint{$entry["type"]["size"]}_t *v)";
|
||||||
|
case "data":
|
||||||
|
return "int {$msg["name"]}Parser_Get{$entry["name"]} ({$msg["name"]}Parser *o, uint8_t **data, int *data_len)";
|
||||||
|
case "constdata":
|
||||||
|
return "int {$msg["name"]}Parser_Get{$entry["name"]} ({$msg["name"]}Parser *o, uint8_t **data)";
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_parser_reset_decl ($msg, $entry)
|
||||||
|
{
|
||||||
|
return "void {$msg["name"]}Parser_Reset{$entry["name"]} ({$msg["name"]}Parser *o)";
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_parser_forward_decl ($msg, $entry)
|
||||||
|
{
|
||||||
|
return "void {$msg["name"]}Parser_Forward{$entry["name"]} ({$msg["name"]}Parser *o)";
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_type_name ($msg, $entry)
|
||||||
|
{
|
||||||
|
switch ($entry["type"]["type"]) {
|
||||||
|
case "uint":
|
||||||
|
return "BPROTO_TYPE_UINT{$entry["type"]["size"]}";
|
||||||
|
case "data":
|
||||||
|
return "BPROTO_TYPE_DATA";
|
||||||
|
case "constdata":
|
||||||
|
return "BPROTO_TYPE_CONSTDATA";
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_finish_assert ($msg, $entry)
|
||||||
|
{
|
||||||
|
switch ($entry["cardinality"]) {
|
||||||
|
case "repeated":
|
||||||
|
return "ASSERT(o->{$entry["name"]}_count >= 0)";
|
||||||
|
case "required repeated":
|
||||||
|
return "ASSERT(o->{$entry["name"]}_count >= 1)";
|
||||||
|
case "optional":
|
||||||
|
return "ASSERT(o->{$entry["name"]}_count >= 0 && o->{$entry["name"]}_count <= 1)";
|
||||||
|
case "required":
|
||||||
|
return "ASSERT(o->{$entry["name"]}_count == 1)";
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_add_count_assert ($msg, $entry)
|
||||||
|
{
|
||||||
|
if (in_array($entry["cardinality"], array("optional", "required"))) {
|
||||||
|
return "ASSERT(o->{$entry["name"]}_count == 0)";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_add_length_assert ($msg, $entry)
|
||||||
|
{
|
||||||
|
if ($entry["type"]["type"] == "data") {
|
||||||
|
return "ASSERT(len >= 0 && len <= UINT32_MAX)";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_size_define ($msg, $entry)
|
||||||
|
{
|
||||||
|
switch ($entry["type"]["type"]) {
|
||||||
|
case "uint":
|
||||||
|
return "#define {$msg["name"]}_SIZE{$entry["name"]} (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint{$entry["type"]["size"]}_s))";
|
||||||
|
case "data":
|
||||||
|
return "#define {$msg["name"]}_SIZE{$entry["name"]}(_len) (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (_len))";
|
||||||
|
case "constdata":
|
||||||
|
return "#define {$msg["name"]}_SIZE{$entry["name"]} (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + ({$entry["type"]["size"]}))";
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generate_header ($name, $directives, $messages) {
|
||||||
|
ob_start();
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
/*
|
||||||
|
DO NOT EDIT THIS FILE!
|
||||||
|
This file was automatically generated by the bproto generator.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <misc/byteorder.h>
|
||||||
|
#include <bproto/BProto.h>
|
||||||
|
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
foreach ($directives as $directive) {
|
||||||
|
if ($directive["type"] == "include") {
|
||||||
|
echo <<<EOD
|
||||||
|
#include "{$directive["file"]}"
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
foreach ($messages as $msg) {
|
||||||
|
|
||||||
|
foreach ($msg["entries"] as $entry) {
|
||||||
|
$def = make_size_define($msg, $entry);
|
||||||
|
echo <<<EOD
|
||||||
|
{$def}
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t *out;
|
||||||
|
int used;
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
foreach ($msg["entries"] as $entry) {
|
||||||
|
echo <<<EOD
|
||||||
|
int {$entry["name"]}_count;
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
} {$msg["name"]}Writer;
|
||||||
|
|
||||||
|
static void {$msg["name"]}Writer_Init ({$msg["name"]}Writer *o, uint8_t *out);
|
||||||
|
static int {$msg["name"]}Writer_Finish ({$msg["name"]}Writer *o);
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
foreach ($msg["entries"] as $entry) {
|
||||||
|
$decl = make_writer_decl($msg, $entry);
|
||||||
|
echo <<<EOD
|
||||||
|
static {$decl};
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t *buf;
|
||||||
|
int buf_len;
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
foreach ($msg["entries"] as $entry) {
|
||||||
|
echo <<<EOD
|
||||||
|
int {$entry["name"]}_start;
|
||||||
|
int {$entry["name"]}_span;
|
||||||
|
int {$entry["name"]}_pos;
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
} {$msg["name"]}Parser;
|
||||||
|
|
||||||
|
static int {$msg["name"]}Parser_Init ({$msg["name"]}Parser *o, uint8_t *buf, int buf_len);
|
||||||
|
static int {$msg["name"]}Parser_GotEverything ({$msg["name"]}Parser *o);
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
foreach ($msg["entries"] as $entry) {
|
||||||
|
$decl = make_parser_decl($msg, $entry);
|
||||||
|
$reset_decl = make_parser_reset_decl($msg, $entry);
|
||||||
|
$forward_decl = make_parser_forward_decl($msg, $entry);
|
||||||
|
echo <<<EOD
|
||||||
|
static {$decl};
|
||||||
|
static {$reset_decl};
|
||||||
|
static {$forward_decl};
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
|
||||||
|
void {$msg["name"]}Writer_Init ({$msg["name"]}Writer *o, uint8_t *out)
|
||||||
|
{
|
||||||
|
o->out = out;
|
||||||
|
o->used = 0;
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
foreach ($msg["entries"] as $entry) {
|
||||||
|
echo <<<EOD
|
||||||
|
o->{$entry["name"]}_count = 0;
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
}
|
||||||
|
|
||||||
|
int {$msg["name"]}Writer_Finish ({$msg["name"]}Writer *o)
|
||||||
|
{
|
||||||
|
ASSERT(o->used >= 0)
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
foreach ($msg["entries"] as $entry) {
|
||||||
|
$ass = make_finish_assert($msg, $entry);
|
||||||
|
echo <<<EOD
|
||||||
|
{$ass}
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
|
||||||
|
return o->used;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
foreach ($msg["entries"] as $entry) {
|
||||||
|
$decl = make_writer_decl($msg, $entry);
|
||||||
|
$type = make_type_name($msg, $entry);
|
||||||
|
$add_count_assert = make_add_count_assert($msg, $entry);
|
||||||
|
$add_length_assert = make_add_length_assert($msg, $entry);
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
{$decl}
|
||||||
|
{
|
||||||
|
ASSERT(o->used >= 0)
|
||||||
|
{$add_count_assert}
|
||||||
|
{$add_length_assert}
|
||||||
|
|
||||||
|
struct BProto_header_s header;
|
||||||
|
header.id = htol16({$entry["id"]});
|
||||||
|
header.type = htol16({$type});
|
||||||
|
memcpy(o->out + o->used, &header, sizeof(header));
|
||||||
|
o->used += sizeof(struct BProto_header_s);
|
||||||
|
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
switch ($entry["type"]["type"]) {
|
||||||
|
case "uint":
|
||||||
|
echo <<<EOD
|
||||||
|
struct BProto_uint{$entry["type"]["size"]}_s data;
|
||||||
|
data.v = htol{$entry["type"]["size"]}(v);
|
||||||
|
memcpy(o->out + o->used, &data, sizeof(data));
|
||||||
|
o->used += sizeof(struct BProto_uint{$entry["type"]["size"]}_s);
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
break;
|
||||||
|
case "data":
|
||||||
|
echo <<<EOD
|
||||||
|
struct BProto_data_header_s data;
|
||||||
|
data.len = htol32(len);
|
||||||
|
memcpy(o->out + o->used, &data, sizeof(data));
|
||||||
|
o->used += sizeof(struct BProto_data_header_s);
|
||||||
|
|
||||||
|
uint8_t *dest = (o->out + o->used);
|
||||||
|
o->used += len;
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
break;
|
||||||
|
case "constdata":
|
||||||
|
echo <<<EOD
|
||||||
|
struct BProto_data_header_s data;
|
||||||
|
data.len = htol32({$entry["type"]["size"]});
|
||||||
|
memcpy(o->out + o->used, &data, sizeof(data));
|
||||||
|
o->used += sizeof(struct BProto_data_header_s);
|
||||||
|
|
||||||
|
uint8_t *dest = (o->out + o->used);
|
||||||
|
o->used += ({$entry["type"]["size"]});
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
|
||||||
|
o->{$entry["name"]}_count++;
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
if (in_array($entry["type"]["type"], array("data", "constdata"))) {
|
||||||
|
echo <<<EOD
|
||||||
|
|
||||||
|
return dest;
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
int {$msg["name"]}Parser_Init ({$msg["name"]}Parser *o, uint8_t *buf, int buf_len)
|
||||||
|
{
|
||||||
|
ASSERT(buf_len >= 0)
|
||||||
|
|
||||||
|
o->buf = buf;
|
||||||
|
o->buf_len = buf_len;
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
foreach ($msg["entries"] as $entry) {
|
||||||
|
echo <<<EOD
|
||||||
|
o->{$entry["name"]}_start = o->buf_len;
|
||||||
|
o->{$entry["name"]}_span = 0;
|
||||||
|
o->{$entry["name"]}_pos = 0;
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
foreach ($msg["entries"] as $entry) {
|
||||||
|
echo <<<EOD
|
||||||
|
int {$entry["name"]}_count = 0;
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
|
||||||
|
int pos = 0;
|
||||||
|
int left = o->buf_len;
|
||||||
|
|
||||||
|
while (left > 0) {
|
||||||
|
int entry_pos = pos;
|
||||||
|
|
||||||
|
if (!(left >= sizeof(struct BProto_header_s))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
struct BProto_header_s header;
|
||||||
|
memcpy(&header, o->buf + pos, sizeof(header));
|
||||||
|
pos += sizeof(struct BProto_header_s);
|
||||||
|
left -= sizeof(struct BProto_header_s);
|
||||||
|
uint16_t type = ltoh16(header.type);
|
||||||
|
uint16_t id = ltoh16(header.id);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
foreach (array(8, 16, 32, 64) as $bits) {
|
||||||
|
echo <<<EOD
|
||||||
|
case BPROTO_TYPE_UINT{$bits}: {
|
||||||
|
if (!(left >= sizeof(struct BProto_uint{$bits}_s))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
pos += sizeof(struct BProto_uint{$bits}_s);
|
||||||
|
left -= sizeof(struct BProto_uint{$bits}_s);
|
||||||
|
|
||||||
|
switch (id) {
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
foreach ($msg["entries"] as $entry) {
|
||||||
|
if (!($entry["type"]["type"] == "uint" && $entry["type"]["size"] == $bits)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$type = make_type_name($msg, $entry);
|
||||||
|
echo <<<EOD
|
||||||
|
case {$entry["id"]}:
|
||||||
|
if (o->{$entry["name"]}_start == o->buf_len) {
|
||||||
|
o->{$entry["name"]}_start = entry_pos;
|
||||||
|
}
|
||||||
|
o->{$entry["name"]}_span = pos - o->{$entry["name"]}_start;
|
||||||
|
{$entry["name"]}_count++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
case BPROTO_TYPE_DATA:
|
||||||
|
case BPROTO_TYPE_CONSTDATA:
|
||||||
|
{
|
||||||
|
if (!(left >= sizeof(struct BProto_data_header_s))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
struct BProto_data_header_s val;
|
||||||
|
memcpy(&val, o->buf + pos, sizeof(val));
|
||||||
|
pos += sizeof(struct BProto_data_header_s);
|
||||||
|
left -= sizeof(struct BProto_data_header_s);
|
||||||
|
|
||||||
|
uint32_t payload_len = ltoh32(val.len);
|
||||||
|
if (!(left >= payload_len)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
pos += payload_len;
|
||||||
|
left -= payload_len;
|
||||||
|
|
||||||
|
switch (id) {
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
foreach ($msg["entries"] as $entry) {
|
||||||
|
if (!in_array($entry["type"]["type"], array("data", "constdata"))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$type = make_type_name($msg, $entry);
|
||||||
|
echo <<<EOD
|
||||||
|
case {$entry["id"]}:
|
||||||
|
if (!(type == {$type})) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
if ($entry["type"]["type"] == "constdata") {
|
||||||
|
echo <<<EOD
|
||||||
|
if (!(payload_len == ({$entry["type"]["size"]}))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
echo <<<EOD
|
||||||
|
if (o->{$entry["name"]}_start == o->buf_len) {
|
||||||
|
o->{$entry["name"]}_start = entry_pos;
|
||||||
|
}
|
||||||
|
o->{$entry["name"]}_span = pos - o->{$entry["name"]}_start;
|
||||||
|
{$entry["name"]}_count++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
foreach ($msg["entries"] as $entry) {
|
||||||
|
$cond = "";
|
||||||
|
switch ($entry["cardinality"]) {
|
||||||
|
case "repeated":
|
||||||
|
break;
|
||||||
|
case "required repeated":
|
||||||
|
$cond = "{$entry["name"]}_count >= 1";
|
||||||
|
break;
|
||||||
|
case "optional":
|
||||||
|
$cond = "{$entry["name"]}_count <= 1";
|
||||||
|
break;
|
||||||
|
case "required":
|
||||||
|
$cond = "{$entry["name"]}_count == 1";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
if ($cond) {
|
||||||
|
echo <<<EOD
|
||||||
|
if (!({$cond})) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int {$msg["name"]}Parser_GotEverything ({$msg["name"]}Parser *o)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
$first = 1;
|
||||||
|
foreach ($msg["entries"] as $entry) {
|
||||||
|
if ($first) {
|
||||||
|
$first = 0;
|
||||||
|
} else {
|
||||||
|
echo <<<EOD
|
||||||
|
&&
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
echo <<<EOD
|
||||||
|
o->{$entry["name"]}_pos == o->{$entry["name"]}_span
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
foreach ($msg["entries"] as $entry) {
|
||||||
|
$decl = make_parser_decl($msg, $entry);
|
||||||
|
$reset_decl = make_parser_reset_decl($msg, $entry);
|
||||||
|
$forward_decl = make_parser_forward_decl($msg, $entry);
|
||||||
|
$type = make_type_name($msg, $entry);
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
{$decl}
|
||||||
|
{
|
||||||
|
ASSERT(o->{$entry["name"]}_pos >= 0)
|
||||||
|
ASSERT(o->{$entry["name"]}_pos <= o->{$entry["name"]}_span)
|
||||||
|
|
||||||
|
int left = o->{$entry["name"]}_span - o->{$entry["name"]}_pos;
|
||||||
|
|
||||||
|
while (left > 0) {
|
||||||
|
ASSERT(left >= sizeof(struct BProto_header_s))
|
||||||
|
struct BProto_header_s header;
|
||||||
|
memcpy(&header, o->buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos, sizeof(header));
|
||||||
|
o->{$entry["name"]}_pos += sizeof(struct BProto_header_s);
|
||||||
|
left -= sizeof(struct BProto_header_s);
|
||||||
|
uint16_t type = ltoh16(header.type);
|
||||||
|
uint16_t id = ltoh16(header.id);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
foreach (array(8, 16, 32, 64) as $bits) {
|
||||||
|
echo <<<EOD
|
||||||
|
case BPROTO_TYPE_UINT{$bits}: {
|
||||||
|
ASSERT(left >= sizeof(struct BProto_uint{$bits}_s))
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
if ($entry["type"]["type"] == "uint" && $entry["type"]["size"] == $bits) {
|
||||||
|
echo <<<EOD
|
||||||
|
struct BProto_uint{$bits}_s val;
|
||||||
|
memcpy(&val, o->buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos, sizeof(val));
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
echo <<<EOD
|
||||||
|
o->{$entry["name"]}_pos += sizeof(struct BProto_uint{$bits}_s);
|
||||||
|
left -= sizeof(struct BProto_uint{$bits}_s);
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
if ($entry["type"]["type"] == "uint" && $entry["type"]["size"] == $bits) {
|
||||||
|
echo <<<EOD
|
||||||
|
|
||||||
|
if (id == {$entry["id"]}) {
|
||||||
|
*v = ltoh{$bits}(val.v);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
} break;
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
case BPROTO_TYPE_DATA:
|
||||||
|
case BPROTO_TYPE_CONSTDATA:
|
||||||
|
{
|
||||||
|
ASSERT(left >= sizeof(struct BProto_data_header_s))
|
||||||
|
struct BProto_data_header_s val;
|
||||||
|
memcpy(&val, o->buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos, sizeof(val));
|
||||||
|
o->{$entry["name"]}_pos += sizeof(struct BProto_data_header_s);
|
||||||
|
left -= sizeof(struct BProto_data_header_s);
|
||||||
|
|
||||||
|
uint32_t payload_len = ltoh32(val.len);
|
||||||
|
ASSERT(left >= payload_len)
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
if ($entry["type"]["type"] == "data" || $entry["type"]["type"] == "constdata") {
|
||||||
|
echo <<<EOD
|
||||||
|
uint8_t *payload = o->buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos;
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
echo <<<EOD
|
||||||
|
o->{$entry["name"]}_pos += payload_len;
|
||||||
|
left -= payload_len;
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
if ($entry["type"]["type"] == "data") {
|
||||||
|
echo <<<EOD
|
||||||
|
|
||||||
|
if (type == BPROTO_TYPE_DATA && id == {$entry["id"]}) {
|
||||||
|
*data = payload;
|
||||||
|
*data_len = payload_len;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
else if ($entry["type"]["type"] == "constdata") {
|
||||||
|
echo <<<EOD
|
||||||
|
|
||||||
|
if (type == BPROTO_TYPE_CONSTDATA && id == {$entry["id"]}) {
|
||||||
|
*data = payload;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
{$reset_decl}
|
||||||
|
{
|
||||||
|
o->{$entry["name"]}_pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
{$forward_decl}
|
||||||
|
{
|
||||||
|
o->{$entry["name"]}_pos = o->{$entry["name"]}_span;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
add_executable(badvpn-client
|
||||||
|
client.c
|
||||||
|
StreamPeerIO.c
|
||||||
|
DatagramPeerIO.c
|
||||||
|
PasswordListener.c
|
||||||
|
DataProto.c
|
||||||
|
FrameDecider.c
|
||||||
|
DPRelay.c
|
||||||
|
DPReceive.c
|
||||||
|
FragmentProtoDisassembler.c
|
||||||
|
FragmentProtoAssembler.c
|
||||||
|
SPProtoEncoder.c
|
||||||
|
SPProtoDecoder.c
|
||||||
|
DataProtoKeepaliveSource.c
|
||||||
|
PeerChat.c
|
||||||
|
SCOutmsgEncoder.c
|
||||||
|
SimpleStreamBuffer.c
|
||||||
|
SinglePacketSource.c
|
||||||
|
)
|
||||||
|
target_link_libraries(badvpn-client system flow flowextra tuntap server_conection security threadwork ${NSPR_LIBRARIES} ${NSS_LIBRARIES})
|
||||||
|
|
||||||
|
install(
|
||||||
|
TARGETS badvpn-client
|
||||||
|
RUNTIME DESTINATION bin
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
FILES badvpn-client.8
|
||||||
|
DESTINATION share/man/man8
|
||||||
|
)
|
|
@ -0,0 +1,324 @@
|
||||||
|
/**
|
||||||
|
* @file DPReceive.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <protocol/dataproto.h>
|
||||||
|
#include <misc/byteorder.h>
|
||||||
|
#include <misc/offset.h>
|
||||||
|
#include <base/BLog.h>
|
||||||
|
|
||||||
|
#include <client/DPReceive.h>
|
||||||
|
|
||||||
|
#include <generated/blog_channel_DPReceive.h>
|
||||||
|
|
||||||
|
static DPReceivePeer * find_peer (DPReceiveDevice *o, peerid_t id)
|
||||||
|
{
|
||||||
|
for (LinkedList1Node *node = LinkedList1_GetFirst(&o->peers_list); node; node = LinkedList1Node_Next(node)) {
|
||||||
|
DPReceivePeer *p = UPPER_OBJECT(node, DPReceivePeer, list_node);
|
||||||
|
if (p->peer_id == id) {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void receiver_recv_handler_send (DPReceiveReceiver *o, uint8_t *packet, int packet_len)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
DPReceivePeer *peer = o->peer;
|
||||||
|
DPReceiveDevice *device = peer->device;
|
||||||
|
ASSERT(packet_len >= 0)
|
||||||
|
ASSERT(packet_len <= device->packet_mtu)
|
||||||
|
|
||||||
|
uint8_t *data = packet;
|
||||||
|
int data_len = packet_len;
|
||||||
|
|
||||||
|
int local = 0;
|
||||||
|
DPReceivePeer *src_peer;
|
||||||
|
DPReceivePeer *relay_dest_peer = NULL;
|
||||||
|
|
||||||
|
// check header
|
||||||
|
if (data_len < sizeof(struct dataproto_header)) {
|
||||||
|
BLog(BLOG_WARNING, "no dataproto header");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
struct dataproto_header header;
|
||||||
|
memcpy(&header, data, sizeof(header));
|
||||||
|
data += sizeof(header);
|
||||||
|
data_len -= sizeof(header);
|
||||||
|
uint8_t flags = ltoh8(header.flags);
|
||||||
|
peerid_t from_id = ltoh16(header.from_id);
|
||||||
|
int num_ids = ltoh16(header.num_peer_ids);
|
||||||
|
|
||||||
|
// check destination ID
|
||||||
|
if (!(num_ids == 0 || num_ids == 1)) {
|
||||||
|
BLog(BLOG_WARNING, "wrong number of destinations");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
peerid_t to_id = 0; // to remove warning
|
||||||
|
if (num_ids == 1) {
|
||||||
|
if (data_len < sizeof(struct dataproto_peer_id)) {
|
||||||
|
BLog(BLOG_WARNING, "missing destination");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
struct dataproto_peer_id id;
|
||||||
|
memcpy(&id, data, sizeof(id));
|
||||||
|
to_id = ltoh16(id.id);
|
||||||
|
data += sizeof(id);
|
||||||
|
data_len -= sizeof(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check remaining data
|
||||||
|
if (data_len > device->device_mtu) {
|
||||||
|
BLog(BLOG_WARNING, "frame too large");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// inform sink of received packet
|
||||||
|
if (peer->dp_sink) {
|
||||||
|
DataProtoSink_Received(peer->dp_sink, !!(flags & DATAPROTO_FLAGS_RECEIVING_KEEPALIVES));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_ids == 1) {
|
||||||
|
// find source peer
|
||||||
|
if (!(src_peer = find_peer(device, from_id))) {
|
||||||
|
BLog(BLOG_INFO, "source peer %d not known", (int)from_id);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// is frame for device or another peer?
|
||||||
|
if (device->have_peer_id && to_id == device->peer_id) {
|
||||||
|
// let the frame decider analyze the frame
|
||||||
|
FrameDeciderPeer_Analyze(src_peer->decider_peer, data, data_len);
|
||||||
|
|
||||||
|
// pass frame to device
|
||||||
|
local = 1;
|
||||||
|
} else {
|
||||||
|
// check if relaying is allowed
|
||||||
|
if (!peer->is_relay_client) {
|
||||||
|
BLog(BLOG_WARNING, "relaying not allowed");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// provided source ID must be the peer sending the frame
|
||||||
|
if (src_peer != peer) {
|
||||||
|
BLog(BLOG_WARNING, "relay source must be the sending peer");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find destination peer
|
||||||
|
DPReceivePeer *dest_peer = find_peer(device, to_id);
|
||||||
|
if (!dest_peer) {
|
||||||
|
BLog(BLOG_INFO, "relay destination peer not known");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// destination cannot be source
|
||||||
|
if (dest_peer == src_peer) {
|
||||||
|
BLog(BLOG_WARNING, "relay destination cannot be the source");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
relay_dest_peer = dest_peer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
// accept packet
|
||||||
|
PacketPassInterface_Done(&o->recv_if);
|
||||||
|
|
||||||
|
// pass packet to device
|
||||||
|
if (local) {
|
||||||
|
o->device->output_func(o->device->output_func_user, data, data_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// relay frame
|
||||||
|
if (relay_dest_peer) {
|
||||||
|
DPRelayRouter_SubmitFrame(&device->relay_router, &src_peer->relay_source, &relay_dest_peer->relay_sink, data, data_len, device->relay_flow_buffer_size, device->relay_flow_inactivity_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int DPReceiveDevice_Init (DPReceiveDevice *o, int device_mtu, DPReceiveDevice_output_func output_func, void *output_func_user, BReactor *reactor, int relay_flow_buffer_size, int relay_flow_inactivity_time)
|
||||||
|
{
|
||||||
|
ASSERT(device_mtu >= 0)
|
||||||
|
ASSERT(device_mtu <= INT_MAX - DATAPROTO_MAX_OVERHEAD)
|
||||||
|
ASSERT(output_func)
|
||||||
|
ASSERT(relay_flow_buffer_size > 0)
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
o->device_mtu = device_mtu;
|
||||||
|
o->output_func = output_func;
|
||||||
|
o->output_func_user = output_func_user;
|
||||||
|
o->reactor = reactor;
|
||||||
|
o->relay_flow_buffer_size = relay_flow_buffer_size;
|
||||||
|
o->relay_flow_inactivity_time = relay_flow_inactivity_time;
|
||||||
|
|
||||||
|
// remember packet MTU
|
||||||
|
o->packet_mtu = DATAPROTO_MAX_OVERHEAD + o->device_mtu;
|
||||||
|
|
||||||
|
// init relay router
|
||||||
|
if (!DPRelayRouter_Init(&o->relay_router, o->device_mtu, o->reactor)) {
|
||||||
|
BLog(BLOG_ERROR, "DPRelayRouter_Init failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// have no peer ID
|
||||||
|
o->have_peer_id = 0;
|
||||||
|
|
||||||
|
// init peers list
|
||||||
|
LinkedList1_Init(&o->peers_list);
|
||||||
|
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail0:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DPReceiveDevice_Free (DPReceiveDevice *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
ASSERT(LinkedList1_IsEmpty(&o->peers_list))
|
||||||
|
|
||||||
|
// free relay router
|
||||||
|
DPRelayRouter_Free(&o->relay_router);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DPReceiveDevice_SetPeerID (DPReceiveDevice *o, peerid_t peer_id)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// remember peer ID
|
||||||
|
o->peer_id = peer_id;
|
||||||
|
o->have_peer_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DPReceivePeer_Init (DPReceivePeer *o, DPReceiveDevice *device, peerid_t peer_id, FrameDeciderPeer *decider_peer, int is_relay_client)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&device->d_obj);
|
||||||
|
ASSERT(is_relay_client == 0 || is_relay_client == 1)
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
o->device = device;
|
||||||
|
o->peer_id = peer_id;
|
||||||
|
o->decider_peer = decider_peer;
|
||||||
|
o->is_relay_client = is_relay_client;
|
||||||
|
|
||||||
|
// init relay source
|
||||||
|
DPRelaySource_Init(&o->relay_source, &device->relay_router, o->peer_id, device->reactor);
|
||||||
|
|
||||||
|
// init relay sink
|
||||||
|
DPRelaySink_Init(&o->relay_sink, o->peer_id);
|
||||||
|
|
||||||
|
// have no sink
|
||||||
|
o->dp_sink = NULL;
|
||||||
|
|
||||||
|
// insert to peers list
|
||||||
|
LinkedList1_Append(&device->peers_list, &o->list_node);
|
||||||
|
|
||||||
|
DebugCounter_Init(&o->d_receivers_ctr);
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DPReceivePeer_Free (DPReceivePeer *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
DebugCounter_Free(&o->d_receivers_ctr);
|
||||||
|
ASSERT(!o->dp_sink)
|
||||||
|
|
||||||
|
// remove from peers list
|
||||||
|
LinkedList1_Remove(&o->device->peers_list, &o->list_node);
|
||||||
|
|
||||||
|
// free relay sink
|
||||||
|
DPRelaySink_Free(&o->relay_sink);
|
||||||
|
|
||||||
|
// free relay source
|
||||||
|
DPRelaySource_Free(&o->relay_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DPReceivePeer_AttachSink (DPReceivePeer *o, DataProtoSink *dp_sink)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(!o->dp_sink)
|
||||||
|
ASSERT(dp_sink)
|
||||||
|
|
||||||
|
// attach relay sink
|
||||||
|
DPRelaySink_Attach(&o->relay_sink, dp_sink);
|
||||||
|
|
||||||
|
o->dp_sink = dp_sink;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DPReceivePeer_DetachSink (DPReceivePeer *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(o->dp_sink)
|
||||||
|
|
||||||
|
// detach relay sink
|
||||||
|
DPRelaySink_Detach(&o->relay_sink);
|
||||||
|
|
||||||
|
o->dp_sink = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DPReceiveReceiver_Init (DPReceiveReceiver *o, DPReceivePeer *peer)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&peer->d_obj);
|
||||||
|
DPReceiveDevice *device = peer->device;
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
o->peer = peer;
|
||||||
|
|
||||||
|
// remember device
|
||||||
|
o->device = device;
|
||||||
|
|
||||||
|
// init receive interface
|
||||||
|
PacketPassInterface_Init(&o->recv_if, device->packet_mtu, (PacketPassInterface_handler_send)receiver_recv_handler_send, o, BReactor_PendingGroup(device->reactor));
|
||||||
|
|
||||||
|
DebugCounter_Increment(&peer->d_receivers_ctr);
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DPReceiveReceiver_Free (DPReceiveReceiver *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
DebugCounter_Decrement(&o->peer->d_receivers_ctr);
|
||||||
|
|
||||||
|
// free receive interface
|
||||||
|
PacketPassInterface_Free(&o->recv_if);
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketPassInterface * DPReceiveReceiver_GetInput (DPReceiveReceiver *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
return &o->recv_if;
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
/**
|
||||||
|
* @file DPReceive.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @section DESCRIPTION
|
||||||
|
*
|
||||||
|
* Receive processing for the VPN client.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_CLIENT_DPRECEIVE_H
|
||||||
|
#define BADVPN_CLIENT_DPRECEIVE_H
|
||||||
|
|
||||||
|
#include <protocol/scproto.h>
|
||||||
|
#include <misc/debugcounter.h>
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <structure/LinkedList1.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <client/DataProto.h>
|
||||||
|
#include <client/DPRelay.h>
|
||||||
|
#include <client/FrameDecider.h>
|
||||||
|
|
||||||
|
typedef void (*DPReceiveDevice_output_func) (void *output_user, uint8_t *data, int data_len);
|
||||||
|
|
||||||
|
struct DPReceiveReceiver_s;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int device_mtu;
|
||||||
|
DPReceiveDevice_output_func output_func;
|
||||||
|
void *output_func_user;
|
||||||
|
BReactor *reactor;
|
||||||
|
int relay_flow_buffer_size;
|
||||||
|
int relay_flow_inactivity_time;
|
||||||
|
int packet_mtu;
|
||||||
|
DPRelayRouter relay_router;
|
||||||
|
int have_peer_id;
|
||||||
|
peerid_t peer_id;
|
||||||
|
LinkedList1 peers_list;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} DPReceiveDevice;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
DPReceiveDevice *device;
|
||||||
|
peerid_t peer_id;
|
||||||
|
FrameDeciderPeer *decider_peer;
|
||||||
|
int is_relay_client;
|
||||||
|
DPRelaySource relay_source;
|
||||||
|
DPRelaySink relay_sink;
|
||||||
|
DataProtoSink *dp_sink;
|
||||||
|
LinkedList1Node list_node;
|
||||||
|
DebugObject d_obj;
|
||||||
|
DebugCounter d_receivers_ctr;
|
||||||
|
} DPReceivePeer;
|
||||||
|
|
||||||
|
typedef struct DPReceiveReceiver_s {
|
||||||
|
DPReceivePeer *peer;
|
||||||
|
DPReceiveDevice *device;
|
||||||
|
PacketPassInterface recv_if;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} DPReceiveReceiver;
|
||||||
|
|
||||||
|
int DPReceiveDevice_Init (DPReceiveDevice *o, int device_mtu, DPReceiveDevice_output_func output_func, void *output_func_user, BReactor *reactor, int relay_flow_buffer_size, int relay_flow_inactivity_time) WARN_UNUSED;
|
||||||
|
void DPReceiveDevice_Free (DPReceiveDevice *o);
|
||||||
|
void DPReceiveDevice_SetPeerID (DPReceiveDevice *o, peerid_t peer_id);
|
||||||
|
|
||||||
|
void DPReceivePeer_Init (DPReceivePeer *o, DPReceiveDevice *device, peerid_t peer_id, FrameDeciderPeer *decider_peer, int is_relay_client);
|
||||||
|
void DPReceivePeer_Free (DPReceivePeer *o);
|
||||||
|
void DPReceivePeer_AttachSink (DPReceivePeer *o, DataProtoSink *dp_sink);
|
||||||
|
void DPReceivePeer_DetachSink (DPReceivePeer *o);
|
||||||
|
|
||||||
|
void DPReceiveReceiver_Init (DPReceiveReceiver *o, DPReceivePeer *peer);
|
||||||
|
void DPReceiveReceiver_Free (DPReceiveReceiver *o);
|
||||||
|
PacketPassInterface * DPReceiveReceiver_GetInput (DPReceiveReceiver *o);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,307 @@
|
||||||
|
/**
|
||||||
|
* @file DPRelay.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <misc/offset.h>
|
||||||
|
#include <base/BLog.h>
|
||||||
|
|
||||||
|
#include <client/DPRelay.h>
|
||||||
|
|
||||||
|
#include <generated/blog_channel_DPRelay.h>
|
||||||
|
|
||||||
|
static void flow_inactivity_handler (struct DPRelay_flow *flow);
|
||||||
|
|
||||||
|
static struct DPRelay_flow * create_flow (DPRelaySource *src, DPRelaySink *sink, int num_packets, int inactivity_time)
|
||||||
|
{
|
||||||
|
ASSERT(num_packets > 0)
|
||||||
|
|
||||||
|
// allocate structure
|
||||||
|
struct DPRelay_flow *flow = (struct DPRelay_flow *)malloc(sizeof(*flow));
|
||||||
|
if (!flow) {
|
||||||
|
BLog(BLOG_ERROR, "relay flow %d->%d: malloc failed", (int)src->source_id, (int)sink->dest_id);
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set src and sink
|
||||||
|
flow->src = src;
|
||||||
|
flow->sink = sink;
|
||||||
|
|
||||||
|
// init DataProtoFlow
|
||||||
|
if (!DataProtoFlow_Init(&flow->dp_flow, &src->router->dp_source, src->source_id, sink->dest_id, num_packets, inactivity_time, flow, (DataProtoFlow_handler_inactivity)flow_inactivity_handler)) {
|
||||||
|
BLog(BLOG_ERROR, "relay flow %d->%d: DataProtoFlow_Init failed", (int)src->source_id, (int)sink->dest_id);
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert to source list
|
||||||
|
LinkedList1_Append(&src->flows_list, &flow->src_list_node);
|
||||||
|
|
||||||
|
// insert to sink list
|
||||||
|
LinkedList1_Append(&sink->flows_list, &flow->sink_list_node);
|
||||||
|
|
||||||
|
// attach flow if needed
|
||||||
|
if (sink->dp_sink) {
|
||||||
|
DataProtoFlow_Attach(&flow->dp_flow, sink->dp_sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
BLog(BLOG_INFO, "relay flow %d->%d: created", (int)src->source_id, (int)sink->dest_id);
|
||||||
|
|
||||||
|
return flow;
|
||||||
|
|
||||||
|
fail1:
|
||||||
|
free(flow);
|
||||||
|
fail0:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_flow (struct DPRelay_flow *flow)
|
||||||
|
{
|
||||||
|
// detach flow if needed
|
||||||
|
if (flow->sink->dp_sink) {
|
||||||
|
DataProtoFlow_Detach(&flow->dp_flow);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove posible router reference
|
||||||
|
if (flow->src->router->current_flow == flow) {
|
||||||
|
flow->src->router->current_flow = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove from sink list
|
||||||
|
LinkedList1_Remove(&flow->sink->flows_list, &flow->sink_list_node);
|
||||||
|
|
||||||
|
// remove from source list
|
||||||
|
LinkedList1_Remove(&flow->src->flows_list, &flow->src_list_node);
|
||||||
|
|
||||||
|
// free DataProtoFlow
|
||||||
|
DataProtoFlow_Free(&flow->dp_flow);
|
||||||
|
|
||||||
|
// free structore
|
||||||
|
free(flow);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void flow_inactivity_handler (struct DPRelay_flow *flow)
|
||||||
|
{
|
||||||
|
BLog(BLOG_INFO, "relay flow %d->%d: timed out", (int)flow->src->source_id, (int)flow->sink->dest_id);
|
||||||
|
|
||||||
|
free_flow(flow);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct DPRelay_flow * source_find_flow (DPRelaySource *o, DPRelaySink *sink)
|
||||||
|
{
|
||||||
|
for (LinkedList1Node *node = LinkedList1_GetFirst(&o->flows_list); node; node = LinkedList1Node_Next(node)) {
|
||||||
|
struct DPRelay_flow *flow = UPPER_OBJECT(node, struct DPRelay_flow, src_list_node);
|
||||||
|
ASSERT(flow->src == o)
|
||||||
|
if (flow->sink == sink) {
|
||||||
|
return flow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void router_dp_source_handler (DPRelayRouter *o, const uint8_t *frame, int frame_len)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
if (!o->current_flow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// route frame to current flow
|
||||||
|
DataProtoFlow_Route(&o->current_flow->dp_flow, 0);
|
||||||
|
|
||||||
|
// set no current flow
|
||||||
|
o->current_flow = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DPRelayRouter_Init (DPRelayRouter *o, int frame_mtu, BReactor *reactor)
|
||||||
|
{
|
||||||
|
ASSERT(frame_mtu >= 0)
|
||||||
|
ASSERT(frame_mtu <= INT_MAX - DATAPROTO_MAX_OVERHEAD)
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
o->frame_mtu = frame_mtu;
|
||||||
|
|
||||||
|
// init BufferWriter
|
||||||
|
BufferWriter_Init(&o->writer, frame_mtu, BReactor_PendingGroup(reactor));
|
||||||
|
|
||||||
|
// init DataProtoSource
|
||||||
|
if (!DataProtoSource_Init(&o->dp_source, BufferWriter_GetOutput(&o->writer), (DataProtoSource_handler)router_dp_source_handler, o, reactor)) {
|
||||||
|
BLog(BLOG_ERROR, "DataProtoSource_Init failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// have no current flow
|
||||||
|
o->current_flow = NULL;
|
||||||
|
|
||||||
|
DebugCounter_Init(&o->d_ctr);
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail1:
|
||||||
|
BufferWriter_Free(&o->writer);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DPRelayRouter_Free (DPRelayRouter *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
DebugCounter_Free(&o->d_ctr);
|
||||||
|
ASSERT(!o->current_flow) // have no sources
|
||||||
|
|
||||||
|
// free DataProtoSource
|
||||||
|
DataProtoSource_Free(&o->dp_source);
|
||||||
|
|
||||||
|
// free BufferWriter
|
||||||
|
BufferWriter_Free(&o->writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DPRelayRouter_SubmitFrame (DPRelayRouter *o, DPRelaySource *src, DPRelaySink *sink, uint8_t *data, int data_len, int num_packets, int inactivity_time)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
DebugObject_Access(&src->d_obj);
|
||||||
|
DebugObject_Access(&sink->d_obj);
|
||||||
|
ASSERT(!o->current_flow)
|
||||||
|
ASSERT(src->router == o)
|
||||||
|
ASSERT(data_len >= 0)
|
||||||
|
ASSERT(data_len <= o->frame_mtu)
|
||||||
|
ASSERT(num_packets > 0)
|
||||||
|
|
||||||
|
// get memory location
|
||||||
|
uint8_t *out;
|
||||||
|
if (!BufferWriter_StartPacket(&o->writer, &out)) {
|
||||||
|
BLog(BLOG_ERROR, "BufferWriter_StartPacket failed for frame %d->%d !?", (int)src->source_id, (int)sink->dest_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write frame
|
||||||
|
memcpy(out, data, data_len);
|
||||||
|
|
||||||
|
// submit frame
|
||||||
|
BufferWriter_EndPacket(&o->writer, data_len);
|
||||||
|
|
||||||
|
// get a flow
|
||||||
|
// this comes _after_ writing the packet, in case flow initialization schedules jobs
|
||||||
|
struct DPRelay_flow *flow = source_find_flow(src, sink);
|
||||||
|
if (!flow) {
|
||||||
|
if (!(flow = create_flow(src, sink, num_packets, inactivity_time))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remember flow so we know where to route the frame in router_dp_source_handler
|
||||||
|
o->current_flow = flow;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DPRelaySource_Init (DPRelaySource *o, DPRelayRouter *router, peerid_t source_id, BReactor *reactor)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&router->d_obj);
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
o->router = router;
|
||||||
|
o->source_id = source_id;
|
||||||
|
|
||||||
|
// init flows list
|
||||||
|
LinkedList1_Init(&o->flows_list);
|
||||||
|
|
||||||
|
DebugCounter_Increment(&o->router->d_ctr);
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DPRelaySource_Free (DPRelaySource *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
DebugCounter_Decrement(&o->router->d_ctr);
|
||||||
|
|
||||||
|
// free flows, detaching them if needed
|
||||||
|
LinkedList1Node *node;
|
||||||
|
while (node = LinkedList1_GetFirst(&o->flows_list)) {
|
||||||
|
struct DPRelay_flow *flow = UPPER_OBJECT(node, struct DPRelay_flow, src_list_node);
|
||||||
|
free_flow(flow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DPRelaySink_Init (DPRelaySink *o, peerid_t dest_id)
|
||||||
|
{
|
||||||
|
// init arguments
|
||||||
|
o->dest_id = dest_id;
|
||||||
|
|
||||||
|
// init flows list
|
||||||
|
LinkedList1_Init(&o->flows_list);
|
||||||
|
|
||||||
|
// have no sink
|
||||||
|
o->dp_sink = NULL;
|
||||||
|
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DPRelaySink_Free (DPRelaySink *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
ASSERT(!o->dp_sink)
|
||||||
|
|
||||||
|
// free flows
|
||||||
|
LinkedList1Node *node;
|
||||||
|
while (node = LinkedList1_GetFirst(&o->flows_list)) {
|
||||||
|
struct DPRelay_flow *flow = UPPER_OBJECT(node, struct DPRelay_flow, sink_list_node);
|
||||||
|
free_flow(flow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DPRelaySink_Attach (DPRelaySink *o, DataProtoSink *dp_sink)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(!o->dp_sink)
|
||||||
|
ASSERT(dp_sink)
|
||||||
|
|
||||||
|
// attach flows
|
||||||
|
for (LinkedList1Node *node = LinkedList1_GetFirst(&o->flows_list); node; node = LinkedList1Node_Next(node)) {
|
||||||
|
struct DPRelay_flow *flow = UPPER_OBJECT(node, struct DPRelay_flow, sink_list_node);
|
||||||
|
DataProtoFlow_Attach(&flow->dp_flow, dp_sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set sink
|
||||||
|
o->dp_sink = dp_sink;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DPRelaySink_Detach (DPRelaySink *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(o->dp_sink)
|
||||||
|
|
||||||
|
// detach flows
|
||||||
|
for (LinkedList1Node *node = LinkedList1_GetFirst(&o->flows_list); node; node = LinkedList1Node_Next(node)) {
|
||||||
|
struct DPRelay_flow *flow = UPPER_OBJECT(node, struct DPRelay_flow, sink_list_node);
|
||||||
|
DataProtoFlow_Detach(&flow->dp_flow);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set no sink
|
||||||
|
o->dp_sink = NULL;
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
/**
|
||||||
|
* @file DPRelay.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_CLIENT_DPRELAY_H
|
||||||
|
#define BADVPN_CLIENT_DPRELAY_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include <protocol/scproto.h>
|
||||||
|
#include <protocol/dataproto.h>
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <structure/LinkedList1.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <flow/BufferWriter.h>
|
||||||
|
#include <client/DataProto.h>
|
||||||
|
|
||||||
|
struct DPRelay_flow;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int frame_mtu;
|
||||||
|
BufferWriter writer;
|
||||||
|
DataProtoSource dp_source;
|
||||||
|
struct DPRelay_flow *current_flow;
|
||||||
|
DebugObject d_obj;
|
||||||
|
DebugCounter d_ctr;
|
||||||
|
} DPRelayRouter;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
DPRelayRouter *router;
|
||||||
|
peerid_t source_id;
|
||||||
|
LinkedList1 flows_list;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} DPRelaySource;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
peerid_t dest_id;
|
||||||
|
LinkedList1 flows_list;
|
||||||
|
DataProtoSink *dp_sink;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} DPRelaySink;
|
||||||
|
|
||||||
|
struct DPRelay_flow {
|
||||||
|
DPRelaySource *src;
|
||||||
|
DPRelaySink *sink;
|
||||||
|
DataProtoFlow dp_flow;
|
||||||
|
LinkedList1Node src_list_node;
|
||||||
|
LinkedList1Node sink_list_node;
|
||||||
|
};
|
||||||
|
|
||||||
|
int DPRelayRouter_Init (DPRelayRouter *o, int frame_mtu, BReactor *reactor) WARN_UNUSED;
|
||||||
|
void DPRelayRouter_Free (DPRelayRouter *o);
|
||||||
|
void DPRelayRouter_SubmitFrame (DPRelayRouter *o, DPRelaySource *src, DPRelaySink *sink, uint8_t *data, int data_len, int num_packets, int inactivity_time);
|
||||||
|
|
||||||
|
void DPRelaySource_Init (DPRelaySource *o, DPRelayRouter *router, peerid_t source_id, BReactor *reactor);
|
||||||
|
void DPRelaySource_Free (DPRelaySource *o);
|
||||||
|
|
||||||
|
void DPRelaySink_Init (DPRelaySink *o, peerid_t dest_id);
|
||||||
|
void DPRelaySink_Free (DPRelaySink *o);
|
||||||
|
void DPRelaySink_Attach (DPRelaySink *o, DataProtoSink *dp_sink);
|
||||||
|
void DPRelaySink_Detach (DPRelaySink *o);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,566 @@
|
||||||
|
/**
|
||||||
|
* @file DataProto.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include <protocol/dataproto.h>
|
||||||
|
#include <misc/byteorder.h>
|
||||||
|
#include <base/BLog.h>
|
||||||
|
|
||||||
|
#include <client/DataProto.h>
|
||||||
|
|
||||||
|
#include <generated/blog_channel_DataProto.h>
|
||||||
|
|
||||||
|
static void monitor_handler (DataProtoSink *o);
|
||||||
|
static void refresh_up_job (DataProtoSink *o);
|
||||||
|
static void receive_timer_handler (DataProtoSink *o);
|
||||||
|
static void notifier_handler (DataProtoSink *o, uint8_t *data, int data_len);
|
||||||
|
static void up_job_handler (DataProtoSink *o);
|
||||||
|
static void flow_buffer_free (struct DataProtoFlow_buffer *b);
|
||||||
|
static void flow_buffer_attach (struct DataProtoFlow_buffer *b, DataProtoSink *sink);
|
||||||
|
static void flow_buffer_detach (struct DataProtoFlow_buffer *b);
|
||||||
|
static void flow_buffer_schedule_detach (struct DataProtoFlow_buffer *b);
|
||||||
|
static void flow_buffer_finish_detach (struct DataProtoFlow_buffer *b);
|
||||||
|
static void flow_buffer_qflow_handler_busy (struct DataProtoFlow_buffer *b);
|
||||||
|
|
||||||
|
void monitor_handler (DataProtoSink *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// send keep-alive
|
||||||
|
PacketRecvBlocker_AllowBlockedPacket(&o->ka_blocker);
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh_up_job (DataProtoSink *o)
|
||||||
|
{
|
||||||
|
if (o->up != o->up_report) {
|
||||||
|
BPending_Set(&o->up_job);
|
||||||
|
} else {
|
||||||
|
BPending_Unset(&o->up_job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void receive_timer_handler (DataProtoSink *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// consider down
|
||||||
|
o->up = 0;
|
||||||
|
|
||||||
|
refresh_up_job(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifier_handler (DataProtoSink *o, uint8_t *data, int data_len)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(data_len >= sizeof(struct dataproto_header))
|
||||||
|
|
||||||
|
int flags = 0;
|
||||||
|
|
||||||
|
// if we are receiving keepalives, set the flag
|
||||||
|
if (BTimer_IsRunning(&o->receive_timer)) {
|
||||||
|
flags |= DATAPROTO_FLAGS_RECEIVING_KEEPALIVES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// modify existing packet here
|
||||||
|
struct dataproto_header header;
|
||||||
|
memcpy(&header, data, sizeof(header));
|
||||||
|
header.flags = hton8(flags);
|
||||||
|
memcpy(data, &header, sizeof(header));
|
||||||
|
}
|
||||||
|
|
||||||
|
void up_job_handler (DataProtoSink *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(o->up != o->up_report)
|
||||||
|
|
||||||
|
o->up_report = o->up;
|
||||||
|
|
||||||
|
o->handler(o->user, o->up);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void source_router_handler (DataProtoSource *o, uint8_t *buf, int recv_len)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(buf)
|
||||||
|
ASSERT(recv_len >= 0)
|
||||||
|
ASSERT(recv_len <= o->frame_mtu)
|
||||||
|
|
||||||
|
// remember packet
|
||||||
|
o->current_buf = buf;
|
||||||
|
o->current_recv_len = recv_len;
|
||||||
|
|
||||||
|
// call handler
|
||||||
|
o->handler(o->user, buf + DATAPROTO_MAX_OVERHEAD, recv_len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void flow_buffer_free (struct DataProtoFlow_buffer *b)
|
||||||
|
{
|
||||||
|
ASSERT(!b->sink)
|
||||||
|
|
||||||
|
// free route buffer
|
||||||
|
RouteBuffer_Free(&b->rbuf);
|
||||||
|
|
||||||
|
// free inactivity monitor
|
||||||
|
if (b->inactivity_time >= 0) {
|
||||||
|
PacketPassInactivityMonitor_Free(&b->monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// free connector
|
||||||
|
PacketPassConnector_Free(&b->connector);
|
||||||
|
|
||||||
|
// free buffer structure
|
||||||
|
free(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flow_buffer_attach (struct DataProtoFlow_buffer *b, DataProtoSink *sink)
|
||||||
|
{
|
||||||
|
ASSERT(!b->sink)
|
||||||
|
|
||||||
|
// init queue flow
|
||||||
|
PacketPassFairQueueFlow_Init(&b->sink_qflow, &sink->queue);
|
||||||
|
|
||||||
|
// connect to queue flow
|
||||||
|
PacketPassConnector_ConnectOutput(&b->connector, PacketPassFairQueueFlow_GetInput(&b->sink_qflow));
|
||||||
|
|
||||||
|
// set DataProto
|
||||||
|
b->sink = sink;
|
||||||
|
}
|
||||||
|
|
||||||
|
void flow_buffer_detach (struct DataProtoFlow_buffer *b)
|
||||||
|
{
|
||||||
|
ASSERT(b->sink)
|
||||||
|
PacketPassFairQueueFlow_AssertFree(&b->sink_qflow);
|
||||||
|
|
||||||
|
// disconnect from queue flow
|
||||||
|
PacketPassConnector_DisconnectOutput(&b->connector);
|
||||||
|
|
||||||
|
// free queue flow
|
||||||
|
PacketPassFairQueueFlow_Free(&b->sink_qflow);
|
||||||
|
|
||||||
|
// clear reference to this buffer in the sink
|
||||||
|
if (b->sink->detaching_buffer == b) {
|
||||||
|
b->sink->detaching_buffer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set no DataProto
|
||||||
|
b->sink = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void flow_buffer_schedule_detach (struct DataProtoFlow_buffer *b)
|
||||||
|
{
|
||||||
|
ASSERT(b->sink)
|
||||||
|
ASSERT(PacketPassFairQueueFlow_IsBusy(&b->sink_qflow))
|
||||||
|
ASSERT(!b->sink->detaching_buffer || b->sink->detaching_buffer == b)
|
||||||
|
|
||||||
|
if (b->sink->detaching_buffer == b) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// request cancel
|
||||||
|
PacketPassFairQueueFlow_RequestCancel(&b->sink_qflow);
|
||||||
|
|
||||||
|
// set busy handler
|
||||||
|
PacketPassFairQueueFlow_SetBusyHandler(&b->sink_qflow, (PacketPassFairQueue_handler_busy)flow_buffer_qflow_handler_busy, b);
|
||||||
|
|
||||||
|
// remember this buffer in the sink so it can handle us if it goes away
|
||||||
|
b->sink->detaching_buffer = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void flow_buffer_finish_detach (struct DataProtoFlow_buffer *b)
|
||||||
|
{
|
||||||
|
ASSERT(b->sink)
|
||||||
|
ASSERT(b->sink->detaching_buffer == b)
|
||||||
|
PacketPassFairQueueFlow_AssertFree(&b->sink_qflow);
|
||||||
|
|
||||||
|
// detach
|
||||||
|
flow_buffer_detach(b);
|
||||||
|
|
||||||
|
if (!b->flow) {
|
||||||
|
// free
|
||||||
|
flow_buffer_free(b);
|
||||||
|
} else if (b->flow->sink_desired) {
|
||||||
|
// attach
|
||||||
|
flow_buffer_attach(b, b->flow->sink_desired);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void flow_buffer_qflow_handler_busy (struct DataProtoFlow_buffer *b)
|
||||||
|
{
|
||||||
|
ASSERT(b->sink)
|
||||||
|
ASSERT(b->sink->detaching_buffer == b)
|
||||||
|
PacketPassFairQueueFlow_AssertFree(&b->sink_qflow);
|
||||||
|
|
||||||
|
flow_buffer_finish_detach(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DataProtoSink_Init (DataProtoSink *o, BReactor *reactor, PacketPassInterface *output, btime_t keepalive_time, btime_t tolerance_time, DataProtoSink_handler handler, void *user)
|
||||||
|
{
|
||||||
|
ASSERT(PacketPassInterface_HasCancel(output))
|
||||||
|
ASSERT(PacketPassInterface_GetMTU(output) >= DATAPROTO_MAX_OVERHEAD)
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
o->reactor = reactor;
|
||||||
|
o->handler = handler;
|
||||||
|
o->user = user;
|
||||||
|
|
||||||
|
// set frame MTU
|
||||||
|
o->frame_mtu = PacketPassInterface_GetMTU(output) - DATAPROTO_MAX_OVERHEAD;
|
||||||
|
|
||||||
|
// init notifier
|
||||||
|
PacketPassNotifier_Init(&o->notifier, output, BReactor_PendingGroup(o->reactor));
|
||||||
|
PacketPassNotifier_SetHandler(&o->notifier, (PacketPassNotifier_handler_notify)notifier_handler, o);
|
||||||
|
|
||||||
|
// init monitor
|
||||||
|
PacketPassInactivityMonitor_Init(&o->monitor, PacketPassNotifier_GetInput(&o->notifier), o->reactor, keepalive_time, (PacketPassInactivityMonitor_handler)monitor_handler, o);
|
||||||
|
PacketPassInactivityMonitor_Force(&o->monitor);
|
||||||
|
|
||||||
|
// init queue
|
||||||
|
if (!PacketPassFairQueue_Init(&o->queue, PacketPassInactivityMonitor_GetInput(&o->monitor), BReactor_PendingGroup(o->reactor), 1, 1)) {
|
||||||
|
BLog(BLOG_ERROR, "PacketPassFairQueue_Init failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init keepalive queue flow
|
||||||
|
PacketPassFairQueueFlow_Init(&o->ka_qflow, &o->queue);
|
||||||
|
|
||||||
|
// init keepalive source
|
||||||
|
DataProtoKeepaliveSource_Init(&o->ka_source, BReactor_PendingGroup(o->reactor));
|
||||||
|
|
||||||
|
// init keepalive blocker
|
||||||
|
PacketRecvBlocker_Init(&o->ka_blocker, DataProtoKeepaliveSource_GetOutput(&o->ka_source), BReactor_PendingGroup(o->reactor));
|
||||||
|
|
||||||
|
// init keepalive buffer
|
||||||
|
if (!SinglePacketBuffer_Init(&o->ka_buffer, PacketRecvBlocker_GetOutput(&o->ka_blocker), PacketPassFairQueueFlow_GetInput(&o->ka_qflow), BReactor_PendingGroup(o->reactor))) {
|
||||||
|
BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed");
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init receive timer
|
||||||
|
BTimer_Init(&o->receive_timer, tolerance_time, (BTimer_handler)receive_timer_handler, o);
|
||||||
|
|
||||||
|
// init handler job
|
||||||
|
BPending_Init(&o->up_job, BReactor_PendingGroup(o->reactor), (BPending_handler)up_job_handler, o);
|
||||||
|
|
||||||
|
// set not up
|
||||||
|
o->up = 0;
|
||||||
|
o->up_report = 0;
|
||||||
|
|
||||||
|
// set no detaching buffer
|
||||||
|
o->detaching_buffer = NULL;
|
||||||
|
|
||||||
|
DebugCounter_Init(&o->d_ctr);
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail2:
|
||||||
|
PacketRecvBlocker_Free(&o->ka_blocker);
|
||||||
|
DataProtoKeepaliveSource_Free(&o->ka_source);
|
||||||
|
PacketPassFairQueueFlow_Free(&o->ka_qflow);
|
||||||
|
PacketPassFairQueue_Free(&o->queue);
|
||||||
|
fail1:
|
||||||
|
PacketPassInactivityMonitor_Free(&o->monitor);
|
||||||
|
PacketPassNotifier_Free(&o->notifier);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataProtoSink_Free (DataProtoSink *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
DebugCounter_Free(&o->d_ctr);
|
||||||
|
|
||||||
|
// allow freeing queue flows
|
||||||
|
PacketPassFairQueue_PrepareFree(&o->queue);
|
||||||
|
|
||||||
|
// release detaching buffer
|
||||||
|
if (o->detaching_buffer) {
|
||||||
|
ASSERT(!o->detaching_buffer->flow || o->detaching_buffer->flow->sink_desired != o)
|
||||||
|
flow_buffer_finish_detach(o->detaching_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// free handler job
|
||||||
|
BPending_Free(&o->up_job);
|
||||||
|
|
||||||
|
// free receive timer
|
||||||
|
BReactor_RemoveTimer(o->reactor, &o->receive_timer);
|
||||||
|
|
||||||
|
// free keepalive buffer
|
||||||
|
SinglePacketBuffer_Free(&o->ka_buffer);
|
||||||
|
|
||||||
|
// free keepalive blocker
|
||||||
|
PacketRecvBlocker_Free(&o->ka_blocker);
|
||||||
|
|
||||||
|
// free keepalive source
|
||||||
|
DataProtoKeepaliveSource_Free(&o->ka_source);
|
||||||
|
|
||||||
|
// free keepalive queue flow
|
||||||
|
PacketPassFairQueueFlow_Free(&o->ka_qflow);
|
||||||
|
|
||||||
|
// free queue
|
||||||
|
PacketPassFairQueue_Free(&o->queue);
|
||||||
|
|
||||||
|
// free monitor
|
||||||
|
PacketPassInactivityMonitor_Free(&o->monitor);
|
||||||
|
|
||||||
|
// free notifier
|
||||||
|
PacketPassNotifier_Free(&o->notifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataProtoSink_Received (DataProtoSink *o, int peer_receiving)
|
||||||
|
{
|
||||||
|
ASSERT(peer_receiving == 0 || peer_receiving == 1)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// reset receive timer
|
||||||
|
BReactor_SetTimer(o->reactor, &o->receive_timer);
|
||||||
|
|
||||||
|
if (!peer_receiving) {
|
||||||
|
// peer reports not receiving, consider down
|
||||||
|
o->up = 0;
|
||||||
|
// send keep-alive to converge faster
|
||||||
|
PacketRecvBlocker_AllowBlockedPacket(&o->ka_blocker);
|
||||||
|
} else {
|
||||||
|
// consider up
|
||||||
|
o->up = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh_up_job(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DataProtoSource_Init (DataProtoSource *o, PacketRecvInterface *input, DataProtoSource_handler handler, void *user, BReactor *reactor)
|
||||||
|
{
|
||||||
|
ASSERT(PacketRecvInterface_GetMTU(input) <= INT_MAX - DATAPROTO_MAX_OVERHEAD)
|
||||||
|
ASSERT(handler)
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
o->handler = handler;
|
||||||
|
o->user = user;
|
||||||
|
o->reactor = reactor;
|
||||||
|
|
||||||
|
// remember frame MTU
|
||||||
|
o->frame_mtu = PacketRecvInterface_GetMTU(input);
|
||||||
|
|
||||||
|
// init router
|
||||||
|
if (!PacketRouter_Init(&o->router, DATAPROTO_MAX_OVERHEAD + o->frame_mtu, DATAPROTO_MAX_OVERHEAD, input, (PacketRouter_handler)source_router_handler, o, BReactor_PendingGroup(reactor))) {
|
||||||
|
BLog(BLOG_ERROR, "PacketRouter_Init failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugCounter_Init(&o->d_ctr);
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail0:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataProtoSource_Free (DataProtoSource *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
DebugCounter_Free(&o->d_ctr);
|
||||||
|
|
||||||
|
// free router
|
||||||
|
PacketRouter_Free(&o->router);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DataProtoFlow_Init (DataProtoFlow *o, DataProtoSource *source, peerid_t source_id, peerid_t dest_id, int num_packets, int inactivity_time, void *user,
|
||||||
|
DataProtoFlow_handler_inactivity handler_inactivity)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&source->d_obj);
|
||||||
|
ASSERT(num_packets > 0)
|
||||||
|
ASSERT(!(inactivity_time >= 0) || handler_inactivity)
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
o->source = source;
|
||||||
|
o->source_id = source_id;
|
||||||
|
o->dest_id = dest_id;
|
||||||
|
|
||||||
|
// set no desired sink
|
||||||
|
o->sink_desired = NULL;
|
||||||
|
|
||||||
|
// allocate buffer structure
|
||||||
|
struct DataProtoFlow_buffer *b = (struct DataProtoFlow_buffer *)malloc(sizeof(*b));
|
||||||
|
if (!b) {
|
||||||
|
BLog(BLOG_ERROR, "malloc failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
o->b = b;
|
||||||
|
|
||||||
|
// set parent
|
||||||
|
b->flow = o;
|
||||||
|
|
||||||
|
// remember inactivity time
|
||||||
|
b->inactivity_time = inactivity_time;
|
||||||
|
|
||||||
|
// init connector
|
||||||
|
PacketPassConnector_Init(&b->connector, DATAPROTO_MAX_OVERHEAD + source->frame_mtu, BReactor_PendingGroup(source->reactor));
|
||||||
|
|
||||||
|
// init inactivity monitor
|
||||||
|
PacketPassInterface *buf_out = PacketPassConnector_GetInput(&b->connector);
|
||||||
|
if (b->inactivity_time >= 0) {
|
||||||
|
PacketPassInactivityMonitor_Init(&b->monitor, buf_out, source->reactor, b->inactivity_time, handler_inactivity, user);
|
||||||
|
buf_out = PacketPassInactivityMonitor_GetInput(&b->monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// init route buffer
|
||||||
|
if (!RouteBuffer_Init(&b->rbuf, DATAPROTO_MAX_OVERHEAD + source->frame_mtu, buf_out, num_packets)) {
|
||||||
|
BLog(BLOG_ERROR, "RouteBuffer_Init failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set no sink
|
||||||
|
b->sink = NULL;
|
||||||
|
|
||||||
|
DebugCounter_Increment(&source->d_ctr);
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail1:
|
||||||
|
if (b->inactivity_time >= 0) {
|
||||||
|
PacketPassInactivityMonitor_Free(&b->monitor);
|
||||||
|
}
|
||||||
|
PacketPassConnector_Free(&b->connector);
|
||||||
|
free(b);
|
||||||
|
fail0:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataProtoFlow_Free (DataProtoFlow *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
DebugCounter_Decrement(&o->source->d_ctr);
|
||||||
|
ASSERT(!o->sink_desired)
|
||||||
|
struct DataProtoFlow_buffer *b = o->b;
|
||||||
|
|
||||||
|
if (b->sink) {
|
||||||
|
if (PacketPassFairQueueFlow_IsBusy(&b->sink_qflow)) {
|
||||||
|
// schedule detach, free buffer after detach
|
||||||
|
flow_buffer_schedule_detach(b);
|
||||||
|
b->flow = NULL;
|
||||||
|
|
||||||
|
// remove inactivity handler
|
||||||
|
if (b->inactivity_time >= 0) {
|
||||||
|
PacketPassInactivityMonitor_SetHandler(&b->monitor, NULL, NULL);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// detach and free buffer now
|
||||||
|
flow_buffer_detach(b);
|
||||||
|
flow_buffer_free(b);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// free buffer
|
||||||
|
flow_buffer_free(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataProtoFlow_Route (DataProtoFlow *o, int more)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
PacketRouter_AssertRoute(&o->source->router);
|
||||||
|
ASSERT(o->source->current_buf)
|
||||||
|
ASSERT(more == 0 || more == 1)
|
||||||
|
struct DataProtoFlow_buffer *b = o->b;
|
||||||
|
|
||||||
|
// write header. Don't set flags, it will be set in notifier_handler.
|
||||||
|
struct dataproto_header header;
|
||||||
|
struct dataproto_peer_id id;
|
||||||
|
header.from_id = htol16(o->source_id);
|
||||||
|
header.num_peer_ids = htol16(1);
|
||||||
|
id.id = htol16(o->dest_id);
|
||||||
|
memcpy(o->source->current_buf, &header, sizeof(header));
|
||||||
|
memcpy(o->source->current_buf + sizeof(header), &id, sizeof(id));
|
||||||
|
|
||||||
|
// route
|
||||||
|
uint8_t *next_buf;
|
||||||
|
if (!PacketRouter_Route(&o->source->router, DATAPROTO_MAX_OVERHEAD + o->source->current_recv_len, &b->rbuf,
|
||||||
|
&next_buf, DATAPROTO_MAX_OVERHEAD, (more ? o->source->current_recv_len : 0)
|
||||||
|
)) {
|
||||||
|
BLog(BLOG_NOTICE, "buffer full: %d->%d", (int)o->source_id, (int)o->dest_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remember next buffer, or don't allow further routing if more==0
|
||||||
|
o->source->current_buf = (more ? next_buf : NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataProtoFlow_Attach (DataProtoFlow *o, DataProtoSink *sink)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
DebugObject_Access(&sink->d_obj);
|
||||||
|
ASSERT(!o->sink_desired)
|
||||||
|
ASSERT(sink)
|
||||||
|
ASSERT(o->source->frame_mtu <= sink->frame_mtu)
|
||||||
|
struct DataProtoFlow_buffer *b = o->b;
|
||||||
|
|
||||||
|
if (b->sink) {
|
||||||
|
if (PacketPassFairQueueFlow_IsBusy(&b->sink_qflow)) {
|
||||||
|
// schedule detach and reattach
|
||||||
|
flow_buffer_schedule_detach(b);
|
||||||
|
} else {
|
||||||
|
// detach and reattach now
|
||||||
|
flow_buffer_detach(b);
|
||||||
|
flow_buffer_attach(b, sink);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// attach
|
||||||
|
flow_buffer_attach(b, sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set desired sink
|
||||||
|
o->sink_desired = sink;
|
||||||
|
|
||||||
|
DebugCounter_Increment(&sink->d_ctr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataProtoFlow_Detach (DataProtoFlow *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(o->sink_desired)
|
||||||
|
struct DataProtoFlow_buffer *b = o->b;
|
||||||
|
ASSERT(b->sink)
|
||||||
|
|
||||||
|
DataProtoSink *sink = o->sink_desired;
|
||||||
|
|
||||||
|
if (PacketPassFairQueueFlow_IsBusy(&b->sink_qflow)) {
|
||||||
|
// schedule detach
|
||||||
|
flow_buffer_schedule_detach(b);
|
||||||
|
} else {
|
||||||
|
// detach now
|
||||||
|
flow_buffer_detach(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set no desired sink
|
||||||
|
o->sink_desired = NULL;
|
||||||
|
|
||||||
|
DebugCounter_Decrement(&sink->d_ctr);
|
||||||
|
}
|
|
@ -0,0 +1,237 @@
|
||||||
|
/**
|
||||||
|
* @file DataProto.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @section DESCRIPTION
|
||||||
|
*
|
||||||
|
* Mudule for frame sending used in the VPN client program.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_CLIENT_DATAPROTO_H
|
||||||
|
#define BADVPN_CLIENT_DATAPROTO_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <misc/debugcounter.h>
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <system/BReactor.h>
|
||||||
|
#include <flow/PacketPassFairQueue.h>
|
||||||
|
#include <flow/PacketPassNotifier.h>
|
||||||
|
#include <flow/PacketRecvBlocker.h>
|
||||||
|
#include <flow/SinglePacketBuffer.h>
|
||||||
|
#include <flow/PacketPassConnector.h>
|
||||||
|
#include <flow/PacketRouter.h>
|
||||||
|
#include <flowextra/PacketPassInactivityMonitor.h>
|
||||||
|
#include <client/DataProtoKeepaliveSource.h>
|
||||||
|
|
||||||
|
typedef void (*DataProtoSink_handler) (void *user, int up);
|
||||||
|
typedef void (*DataProtoSource_handler) (void *user, const uint8_t *frame, int frame_len);
|
||||||
|
typedef void (*DataProtoFlow_handler_inactivity) (void *user);
|
||||||
|
|
||||||
|
struct DataProtoFlow_buffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frame destination.
|
||||||
|
* Represents a peer as a destination for sending frames to.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
BReactor *reactor;
|
||||||
|
int frame_mtu;
|
||||||
|
PacketPassFairQueue queue;
|
||||||
|
PacketPassInactivityMonitor monitor;
|
||||||
|
PacketPassNotifier notifier;
|
||||||
|
DataProtoKeepaliveSource ka_source;
|
||||||
|
PacketRecvBlocker ka_blocker;
|
||||||
|
SinglePacketBuffer ka_buffer;
|
||||||
|
PacketPassFairQueueFlow ka_qflow;
|
||||||
|
BTimer receive_timer;
|
||||||
|
int up;
|
||||||
|
int up_report;
|
||||||
|
DataProtoSink_handler handler;
|
||||||
|
void *user;
|
||||||
|
BPending up_job;
|
||||||
|
struct DataProtoFlow_buffer *detaching_buffer;
|
||||||
|
DebugObject d_obj;
|
||||||
|
DebugCounter d_ctr;
|
||||||
|
} DataProtoSink;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receives frames from a {@link PacketRecvInterface} input and
|
||||||
|
* allows the user to route them to buffers in {@link DataProtoFlow}'s.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
DataProtoSource_handler handler;
|
||||||
|
void *user;
|
||||||
|
BReactor *reactor;
|
||||||
|
int frame_mtu;
|
||||||
|
PacketRouter router;
|
||||||
|
uint8_t *current_buf;
|
||||||
|
int current_recv_len;
|
||||||
|
DebugObject d_obj;
|
||||||
|
DebugCounter d_ctr;
|
||||||
|
} DataProtoSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains a buffer for frames from a specific peer to a specific peer.
|
||||||
|
* Receives frames from a {@link DataProtoSource} as routed by the user.
|
||||||
|
* Can be attached to a {@link DataProtoSink} to send out frames.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
DataProtoSource *source;
|
||||||
|
peerid_t source_id;
|
||||||
|
peerid_t dest_id;
|
||||||
|
DataProtoSink *sink_desired;
|
||||||
|
struct DataProtoFlow_buffer *b;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} DataProtoFlow;
|
||||||
|
|
||||||
|
struct DataProtoFlow_buffer {
|
||||||
|
DataProtoFlow *flow;
|
||||||
|
int inactivity_time;
|
||||||
|
RouteBuffer rbuf;
|
||||||
|
PacketPassInactivityMonitor monitor;
|
||||||
|
PacketPassConnector connector;
|
||||||
|
DataProtoSink *sink;
|
||||||
|
PacketPassFairQueueFlow sink_qflow;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the sink.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param reactor reactor we live in
|
||||||
|
* @param output output interface. Must support cancel functionality. Its MTU must be
|
||||||
|
* >=DATAPROTO_MAX_OVERHEAD.
|
||||||
|
* @param keepalive_time keepalive time
|
||||||
|
* @param tolerance_time after how long of not having received anything from the peer
|
||||||
|
* to consider the link down
|
||||||
|
* @param handler up state handler
|
||||||
|
* @param user value to pass to handler
|
||||||
|
* @return 1 on success, 0 on failure
|
||||||
|
*/
|
||||||
|
int DataProtoSink_Init (DataProtoSink *o, BReactor *reactor, PacketPassInterface *output, btime_t keepalive_time, btime_t tolerance_time, DataProtoSink_handler handler, void *user) WARN_UNUSED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the sink.
|
||||||
|
* There must be no local sources attached.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void DataProtoSink_Free (DataProtoSink *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the sink that a packet was received from the peer.
|
||||||
|
* Must not be in freeing state.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param peer_receiving whether the DATAPROTO_FLAGS_RECEIVING_KEEPALIVES flag was set in the packet.
|
||||||
|
* Must be 0 or 1.
|
||||||
|
*/
|
||||||
|
void DataProtoSink_Received (DataProtoSink *o, int peer_receiving);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiazes the source.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param input frame input. Its input MTU must be <= INT_MAX - DATAPROTO_MAX_OVERHEAD.
|
||||||
|
* @param handler handler called when a frame arrives to allow the user to route it to
|
||||||
|
* appropriate {@link DataProtoFlow}'s.
|
||||||
|
* @param user value passed to handler
|
||||||
|
* @param reactor reactor we live in
|
||||||
|
* @return 1 on success, 0 on failure
|
||||||
|
*/
|
||||||
|
int DataProtoSource_Init (DataProtoSource *o, PacketRecvInterface *input, DataProtoSource_handler handler, void *user, BReactor *reactor) WARN_UNUSED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the source.
|
||||||
|
* There must be no {@link DataProtoFlow}'s using this source.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void DataProtoSource_Free (DataProtoSource *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the flow.
|
||||||
|
* The flow is initialized in not attached state.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param source source to receive frames from
|
||||||
|
* @param source_id source peer ID to encode in the headers (i.e. our ID)
|
||||||
|
* @param dest_id destination peer ID to encode in the headers (i.e. ID if the peer this
|
||||||
|
* flow belongs to)
|
||||||
|
* @param num_packets number of packets the buffer should hold. Must be >0.
|
||||||
|
* @param inactivity_time milliseconds of output inactivity after which to call the
|
||||||
|
* inactivity handler; <0 to disable. Note that the flow is considered
|
||||||
|
* active as long as its buffer is non-empty, even if is not attached to
|
||||||
|
* a {@link DataProtoSink}.
|
||||||
|
* @param user value to pass to handler
|
||||||
|
* @param handler_inactivity inactivity handler, if inactivity_time >=0
|
||||||
|
* @return 1 on success, 0 on failure
|
||||||
|
*/
|
||||||
|
int DataProtoFlow_Init (DataProtoFlow *o, DataProtoSource *source, peerid_t source_id, peerid_t dest_id, int num_packets, int inactivity_time, void *user,
|
||||||
|
DataProtoFlow_handler_inactivity handler_inactivity) WARN_UNUSED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the flow.
|
||||||
|
* The flow must be in not attached state.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void DataProtoFlow_Free (DataProtoFlow *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Routes a frame from the flow's source to this flow.
|
||||||
|
* Must be called from within the job context of the {@link DataProtoSource_handler} handler.
|
||||||
|
* Must not be called after this has been called with more=0 for the current frame.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param more whether the current frame may have to be routed to more
|
||||||
|
* flows. If 0, must not be called again until the handler is
|
||||||
|
* called for the next frame. Must be 0 or 1.
|
||||||
|
*/
|
||||||
|
void DataProtoFlow_Route (DataProtoFlow *o, int more);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches the flow to a sink.
|
||||||
|
* The flow must be in not attached state.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param sink sink to attach to. This flow's frame_mtu must be <=
|
||||||
|
* (output MTU of sink) - DATAPROTO_MAX_OVERHEAD.
|
||||||
|
*/
|
||||||
|
void DataProtoFlow_Attach (DataProtoFlow *o, DataProtoSink *sink);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detaches the flow from a destination.
|
||||||
|
* The flow must be in attached state.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void DataProtoFlow_Detach (DataProtoFlow *o);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,72 @@
|
||||||
|
/**
|
||||||
|
* @file DataProtoKeepaliveSource.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <protocol/dataproto.h>
|
||||||
|
#include <misc/byteorder.h>
|
||||||
|
|
||||||
|
#include "DataProtoKeepaliveSource.h"
|
||||||
|
|
||||||
|
static void output_handler_recv (DataProtoKeepaliveSource *o, uint8_t *data)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
struct dataproto_header header;
|
||||||
|
header.flags = htol8(0);
|
||||||
|
header.from_id = htol16(0);
|
||||||
|
header.num_peer_ids = htol16(0);
|
||||||
|
memcpy(data, &header, sizeof(header));
|
||||||
|
|
||||||
|
// finish packet
|
||||||
|
PacketRecvInterface_Done(&o->output, sizeof(struct dataproto_header));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataProtoKeepaliveSource_Init (DataProtoKeepaliveSource *o, BPendingGroup *pg)
|
||||||
|
{
|
||||||
|
// init output
|
||||||
|
PacketRecvInterface_Init(&o->output, sizeof(struct dataproto_header), (PacketRecvInterface_handler_recv)output_handler_recv, o, pg);
|
||||||
|
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataProtoKeepaliveSource_Free (DataProtoKeepaliveSource *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
|
||||||
|
// free output
|
||||||
|
PacketRecvInterface_Free(&o->output);
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketRecvInterface * DataProtoKeepaliveSource_GetOutput (DataProtoKeepaliveSource *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
return &o->output;
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
/**
|
||||||
|
* @file DataProtoKeepaliveSource.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @section DESCRIPTION
|
||||||
|
*
|
||||||
|
* A {@link PacketRecvInterface} source which provides DataProto keepalive packets.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_DATAPROTOKEEPALIVESOURCE_H
|
||||||
|
#define BADVPN_DATAPROTOKEEPALIVESOURCE_H
|
||||||
|
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <flow/PacketRecvInterface.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link PacketRecvInterface} source which provides DataProto keepalive packets.
|
||||||
|
* These packets have no payload, no destination peers and flags zero.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
DebugObject d_obj;
|
||||||
|
PacketRecvInterface output;
|
||||||
|
} DataProtoKeepaliveSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the object.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param pg pending group
|
||||||
|
*/
|
||||||
|
void DataProtoKeepaliveSource_Init (DataProtoKeepaliveSource *o, BPendingGroup *pg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the object.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void DataProtoKeepaliveSource_Free (DataProtoKeepaliveSource *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the output interface.
|
||||||
|
* The MTU of the output interface will be sizeof(struct dataproto_header).
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @return output interface
|
||||||
|
*/
|
||||||
|
PacketRecvInterface * DataProtoKeepaliveSource_GetOutput (DataProtoKeepaliveSource *o);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,425 @@
|
||||||
|
/**
|
||||||
|
* @file DatagramPeerIO.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <client/DatagramPeerIO.h>
|
||||||
|
|
||||||
|
#include <generated/blog_channel_DatagramPeerIO.h>
|
||||||
|
|
||||||
|
#define DATAGRAMPEERIO_MODE_NONE 0
|
||||||
|
#define DATAGRAMPEERIO_MODE_CONNECT 1
|
||||||
|
#define DATAGRAMPEERIO_MODE_BIND 2
|
||||||
|
|
||||||
|
#define PeerLog(_o, ...) BLog_LogViaFunc((_o)->logfunc, (_o)->user, BLOG_CURRENT_CHANNEL, __VA_ARGS__)
|
||||||
|
|
||||||
|
static void init_io (DatagramPeerIO *o);
|
||||||
|
static void free_io (DatagramPeerIO *o);
|
||||||
|
static void dgram_handler (DatagramPeerIO *o, int event);
|
||||||
|
static void reset_mode (DatagramPeerIO *o);
|
||||||
|
static void recv_decoder_notifier_handler (DatagramPeerIO *o, uint8_t *data, int data_len);
|
||||||
|
|
||||||
|
void init_io (DatagramPeerIO *o)
|
||||||
|
{
|
||||||
|
// init dgram recv interface
|
||||||
|
BDatagram_RecvAsync_Init(&o->dgram, o->effective_socket_mtu);
|
||||||
|
|
||||||
|
// connect source
|
||||||
|
PacketRecvConnector_ConnectInput(&o->recv_connector, BDatagram_RecvAsync_GetIf(&o->dgram));
|
||||||
|
|
||||||
|
// init dgram send interface
|
||||||
|
BDatagram_SendAsync_Init(&o->dgram, o->effective_socket_mtu);
|
||||||
|
|
||||||
|
// connect sink
|
||||||
|
PacketPassConnector_ConnectOutput(&o->send_connector, BDatagram_SendAsync_GetIf(&o->dgram));
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_io (DatagramPeerIO *o)
|
||||||
|
{
|
||||||
|
// disconnect sink
|
||||||
|
PacketPassConnector_DisconnectOutput(&o->send_connector);
|
||||||
|
|
||||||
|
// free dgram send interface
|
||||||
|
BDatagram_SendAsync_Free(&o->dgram);
|
||||||
|
|
||||||
|
// disconnect source
|
||||||
|
PacketRecvConnector_DisconnectInput(&o->recv_connector);
|
||||||
|
|
||||||
|
// free dgram recv interface
|
||||||
|
BDatagram_RecvAsync_Free(&o->dgram);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dgram_handler (DatagramPeerIO *o, int event)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(o->mode == DATAGRAMPEERIO_MODE_CONNECT || o->mode == DATAGRAMPEERIO_MODE_BIND)
|
||||||
|
|
||||||
|
PeerLog(o, BLOG_NOTICE, "error");
|
||||||
|
|
||||||
|
// reset mode
|
||||||
|
reset_mode(o);
|
||||||
|
|
||||||
|
// report error
|
||||||
|
if (o->handler_error) {
|
||||||
|
o->handler_error(o->user);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_mode (DatagramPeerIO *o)
|
||||||
|
{
|
||||||
|
ASSERT(o->mode == DATAGRAMPEERIO_MODE_NONE || o->mode == DATAGRAMPEERIO_MODE_CONNECT || o->mode == DATAGRAMPEERIO_MODE_BIND)
|
||||||
|
|
||||||
|
if (o->mode == DATAGRAMPEERIO_MODE_NONE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove recv notifier handler
|
||||||
|
PacketPassNotifier_SetHandler(&o->recv_notifier, NULL, NULL);
|
||||||
|
|
||||||
|
// free I/O
|
||||||
|
free_io(o);
|
||||||
|
|
||||||
|
// free datagram object
|
||||||
|
BDatagram_Free(&o->dgram);
|
||||||
|
|
||||||
|
// set mode
|
||||||
|
o->mode = DATAGRAMPEERIO_MODE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void recv_decoder_notifier_handler (DatagramPeerIO *o, uint8_t *data, int data_len)
|
||||||
|
{
|
||||||
|
ASSERT(o->mode == DATAGRAMPEERIO_MODE_BIND)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// obtain addresses from last received packet
|
||||||
|
BAddr addr;
|
||||||
|
BIPAddr local_addr;
|
||||||
|
ASSERT_EXECUTE(BDatagram_GetLastReceiveAddrs(&o->dgram, &addr, &local_addr))
|
||||||
|
|
||||||
|
// check address family just in case
|
||||||
|
if (!BDatagram_AddressFamilySupported(addr.type)) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "unsupported receive address");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update addresses
|
||||||
|
BDatagram_SetSendAddrs(&o->dgram, addr, local_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DatagramPeerIO_Init (
|
||||||
|
DatagramPeerIO *o,
|
||||||
|
BReactor *reactor,
|
||||||
|
int payload_mtu,
|
||||||
|
int socket_mtu,
|
||||||
|
struct spproto_security_params sp_params,
|
||||||
|
btime_t latency,
|
||||||
|
int num_frames,
|
||||||
|
PacketPassInterface *recv_userif,
|
||||||
|
int otp_warning_count,
|
||||||
|
BThreadWorkDispatcher *twd,
|
||||||
|
void *user,
|
||||||
|
BLog_logfunc logfunc,
|
||||||
|
DatagramPeerIO_handler_error handler_error,
|
||||||
|
DatagramPeerIO_handler_otp_warning handler_otp_warning,
|
||||||
|
DatagramPeerIO_handler_otp_ready handler_otp_ready
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ASSERT(payload_mtu >= 0)
|
||||||
|
ASSERT(socket_mtu >= 0)
|
||||||
|
spproto_assert_security_params(sp_params);
|
||||||
|
ASSERT(num_frames > 0)
|
||||||
|
ASSERT(PacketPassInterface_GetMTU(recv_userif) >= payload_mtu)
|
||||||
|
if (SPPROTO_HAVE_OTP(sp_params)) {
|
||||||
|
ASSERT(otp_warning_count > 0)
|
||||||
|
ASSERT(otp_warning_count <= sp_params.otp_num)
|
||||||
|
}
|
||||||
|
|
||||||
|
// set parameters
|
||||||
|
o->reactor = reactor;
|
||||||
|
o->payload_mtu = payload_mtu;
|
||||||
|
o->sp_params = sp_params;
|
||||||
|
o->user = user;
|
||||||
|
o->logfunc = logfunc;
|
||||||
|
o->handler_error = handler_error;
|
||||||
|
|
||||||
|
// check num frames (for FragmentProtoAssembler)
|
||||||
|
if (num_frames >= FPA_MAX_TIME) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "num_frames is too big");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check payload MTU (for FragmentProto)
|
||||||
|
if (o->payload_mtu > UINT16_MAX) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "payload MTU is too big");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate SPProto payload MTU
|
||||||
|
if ((o->spproto_payload_mtu = spproto_payload_mtu_for_carrier_mtu(o->sp_params, socket_mtu)) <= (int)sizeof(struct fragmentproto_chunk_header)) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "socket MTU is too small");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate effective socket MTU
|
||||||
|
if ((o->effective_socket_mtu = spproto_carrier_mtu_for_payload_mtu(o->sp_params, o->spproto_payload_mtu)) < 0) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "spproto_carrier_mtu_for_payload_mtu failed !?");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init receiving
|
||||||
|
|
||||||
|
// init assembler
|
||||||
|
if (!FragmentProtoAssembler_Init(&o->recv_assembler, o->spproto_payload_mtu, recv_userif, num_frames, fragmentproto_max_chunks_for_frame(o->spproto_payload_mtu, o->payload_mtu),
|
||||||
|
BReactor_PendingGroup(o->reactor), o->user, o->logfunc
|
||||||
|
)) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "FragmentProtoAssembler_Init failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init notifier
|
||||||
|
PacketPassNotifier_Init(&o->recv_notifier, FragmentProtoAssembler_GetInput(&o->recv_assembler), BReactor_PendingGroup(o->reactor));
|
||||||
|
|
||||||
|
// init decoder
|
||||||
|
if (!SPProtoDecoder_Init(&o->recv_decoder, PacketPassNotifier_GetInput(&o->recv_notifier), o->sp_params, 2, BReactor_PendingGroup(o->reactor), twd, o->user, o->logfunc)) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "SPProtoDecoder_Init failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
SPProtoDecoder_SetHandlers(&o->recv_decoder, handler_otp_ready, user);
|
||||||
|
|
||||||
|
// init connector
|
||||||
|
PacketRecvConnector_Init(&o->recv_connector, o->effective_socket_mtu, BReactor_PendingGroup(o->reactor));
|
||||||
|
|
||||||
|
// init buffer
|
||||||
|
if (!SinglePacketBuffer_Init(&o->recv_buffer, PacketRecvConnector_GetOutput(&o->recv_connector), SPProtoDecoder_GetInput(&o->recv_decoder), BReactor_PendingGroup(o->reactor))) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "SinglePacketBuffer_Init failed");
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init sending base
|
||||||
|
|
||||||
|
// init disassembler
|
||||||
|
FragmentProtoDisassembler_Init(&o->send_disassembler, o->reactor, o->payload_mtu, o->spproto_payload_mtu, -1, latency);
|
||||||
|
|
||||||
|
// init encoder
|
||||||
|
if (!SPProtoEncoder_Init(&o->send_encoder, FragmentProtoDisassembler_GetOutput(&o->send_disassembler), o->sp_params, otp_warning_count, BReactor_PendingGroup(o->reactor), twd)) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "SPProtoEncoder_Init failed");
|
||||||
|
goto fail3;
|
||||||
|
}
|
||||||
|
SPProtoEncoder_SetHandlers(&o->send_encoder, handler_otp_warning, user);
|
||||||
|
|
||||||
|
// init connector
|
||||||
|
PacketPassConnector_Init(&o->send_connector, o->effective_socket_mtu, BReactor_PendingGroup(o->reactor));
|
||||||
|
|
||||||
|
// init buffer
|
||||||
|
if (!SinglePacketBuffer_Init(&o->send_buffer, SPProtoEncoder_GetOutput(&o->send_encoder), PacketPassConnector_GetInput(&o->send_connector), BReactor_PendingGroup(o->reactor))) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "SinglePacketBuffer_Init failed");
|
||||||
|
goto fail4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set mode
|
||||||
|
o->mode = DATAGRAMPEERIO_MODE_NONE;
|
||||||
|
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail4:
|
||||||
|
PacketPassConnector_Free(&o->send_connector);
|
||||||
|
SPProtoEncoder_Free(&o->send_encoder);
|
||||||
|
fail3:
|
||||||
|
FragmentProtoDisassembler_Free(&o->send_disassembler);
|
||||||
|
SinglePacketBuffer_Free(&o->recv_buffer);
|
||||||
|
fail2:
|
||||||
|
PacketRecvConnector_Free(&o->recv_connector);
|
||||||
|
SPProtoDecoder_Free(&o->recv_decoder);
|
||||||
|
fail1:
|
||||||
|
PacketPassNotifier_Free(&o->recv_notifier);
|
||||||
|
FragmentProtoAssembler_Free(&o->recv_assembler);
|
||||||
|
fail0:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatagramPeerIO_Free (DatagramPeerIO *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
|
||||||
|
// reset mode
|
||||||
|
reset_mode(o);
|
||||||
|
|
||||||
|
// free sending base
|
||||||
|
SinglePacketBuffer_Free(&o->send_buffer);
|
||||||
|
PacketPassConnector_Free(&o->send_connector);
|
||||||
|
SPProtoEncoder_Free(&o->send_encoder);
|
||||||
|
FragmentProtoDisassembler_Free(&o->send_disassembler);
|
||||||
|
|
||||||
|
// free receiving
|
||||||
|
SinglePacketBuffer_Free(&o->recv_buffer);
|
||||||
|
PacketRecvConnector_Free(&o->recv_connector);
|
||||||
|
SPProtoDecoder_Free(&o->recv_decoder);
|
||||||
|
PacketPassNotifier_Free(&o->recv_notifier);
|
||||||
|
FragmentProtoAssembler_Free(&o->recv_assembler);
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketPassInterface * DatagramPeerIO_GetSendInput (DatagramPeerIO *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
return FragmentProtoDisassembler_GetInput(&o->send_disassembler);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DatagramPeerIO_Connect (DatagramPeerIO *o, BAddr addr)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// reset mode
|
||||||
|
reset_mode(o);
|
||||||
|
|
||||||
|
// check address
|
||||||
|
if (!BDatagram_AddressFamilySupported(addr.type)) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "BDatagram_AddressFamilySupported failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init dgram
|
||||||
|
if (!BDatagram_Init(&o->dgram, addr.type, o->reactor, o, (BDatagram_handler)dgram_handler)) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "BDatagram_Init failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set send address
|
||||||
|
BIPAddr local_addr;
|
||||||
|
BIPAddr_InitInvalid(&local_addr);
|
||||||
|
BDatagram_SetSendAddrs(&o->dgram, addr, local_addr);
|
||||||
|
|
||||||
|
// init I/O
|
||||||
|
init_io(o);
|
||||||
|
|
||||||
|
// set mode
|
||||||
|
o->mode = DATAGRAMPEERIO_MODE_CONNECT;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail0:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DatagramPeerIO_Bind (DatagramPeerIO *o, BAddr addr)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(BDatagram_AddressFamilySupported(addr.type))
|
||||||
|
|
||||||
|
// reset mode
|
||||||
|
reset_mode(o);
|
||||||
|
|
||||||
|
// init dgram
|
||||||
|
if (!BDatagram_Init(&o->dgram, addr.type, o->reactor, o, (BDatagram_handler)dgram_handler)) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "BDatagram_Init failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bind dgram
|
||||||
|
if (!BDatagram_Bind(&o->dgram, addr)) {
|
||||||
|
PeerLog(o, BLOG_INFO, "BDatagram_Bind failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init I/O
|
||||||
|
init_io(o);
|
||||||
|
|
||||||
|
// set recv notifier handler
|
||||||
|
PacketPassNotifier_SetHandler(&o->recv_notifier, (PacketPassNotifier_handler_notify)recv_decoder_notifier_handler, o);
|
||||||
|
|
||||||
|
// set mode
|
||||||
|
o->mode = DATAGRAMPEERIO_MODE_BIND;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail1:
|
||||||
|
BDatagram_Free(&o->dgram);
|
||||||
|
fail0:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatagramPeerIO_SetEncryptionKey (DatagramPeerIO *o, uint8_t *encryption_key)
|
||||||
|
{
|
||||||
|
ASSERT(SPPROTO_HAVE_ENCRYPTION(o->sp_params))
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// set sending key
|
||||||
|
SPProtoEncoder_SetEncryptionKey(&o->send_encoder, encryption_key);
|
||||||
|
|
||||||
|
// set receiving key
|
||||||
|
SPProtoDecoder_SetEncryptionKey(&o->recv_decoder, encryption_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatagramPeerIO_RemoveEncryptionKey (DatagramPeerIO *o)
|
||||||
|
{
|
||||||
|
ASSERT(SPPROTO_HAVE_ENCRYPTION(o->sp_params))
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// remove sending key
|
||||||
|
SPProtoEncoder_RemoveEncryptionKey(&o->send_encoder);
|
||||||
|
|
||||||
|
// remove receiving key
|
||||||
|
SPProtoDecoder_RemoveEncryptionKey(&o->recv_decoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatagramPeerIO_SetOTPSendSeed (DatagramPeerIO *o, uint16_t seed_id, uint8_t *key, uint8_t *iv)
|
||||||
|
{
|
||||||
|
ASSERT(SPPROTO_HAVE_OTP(o->sp_params))
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// set sending seed
|
||||||
|
SPProtoEncoder_SetOTPSeed(&o->send_encoder, seed_id, key, iv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatagramPeerIO_RemoveOTPSendSeed (DatagramPeerIO *o)
|
||||||
|
{
|
||||||
|
ASSERT(SPPROTO_HAVE_OTP(o->sp_params))
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// remove sending seed
|
||||||
|
SPProtoEncoder_RemoveOTPSeed(&o->send_encoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatagramPeerIO_AddOTPRecvSeed (DatagramPeerIO *o, uint16_t seed_id, uint8_t *key, uint8_t *iv)
|
||||||
|
{
|
||||||
|
ASSERT(SPPROTO_HAVE_OTP(o->sp_params))
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// add receiving seed
|
||||||
|
SPProtoDecoder_AddOTPSeed(&o->recv_decoder, seed_id, key, iv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatagramPeerIO_RemoveOTPRecvSeeds (DatagramPeerIO *o)
|
||||||
|
{
|
||||||
|
ASSERT(SPPROTO_HAVE_OTP(o->sp_params))
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// remove receiving seeds
|
||||||
|
SPProtoDecoder_RemoveOTPSeeds(&o->recv_decoder);
|
||||||
|
}
|
|
@ -0,0 +1,271 @@
|
||||||
|
/**
|
||||||
|
* @file DatagramPeerIO.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @section DESCRIPTION
|
||||||
|
*
|
||||||
|
* Object for comminicating with a peer using a datagram socket.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_CLIENT_DATAGRAMPEERIO_H
|
||||||
|
#define BADVPN_CLIENT_DATAGRAMPEERIO_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <protocol/spproto.h>
|
||||||
|
#include <protocol/fragmentproto.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <base/BLog.h>
|
||||||
|
#include <system/BReactor.h>
|
||||||
|
#include <system/BAddr.h>
|
||||||
|
#include <system/BDatagram.h>
|
||||||
|
#include <system/BTime.h>
|
||||||
|
#include <flow/PacketPassInterface.h>
|
||||||
|
#include <flow/PacketPassConnector.h>
|
||||||
|
#include <flow/SinglePacketBuffer.h>
|
||||||
|
#include <flow/PacketRecvConnector.h>
|
||||||
|
#include <flow/PacketPassNotifier.h>
|
||||||
|
#include <client/FragmentProtoDisassembler.h>
|
||||||
|
#include <client/FragmentProtoAssembler.h>
|
||||||
|
#include <client/SPProtoEncoder.h>
|
||||||
|
#include <client/SPProtoDecoder.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function invoked when an error occurs with the peer connection.
|
||||||
|
* The object has entered default state.
|
||||||
|
* May be called from within a sending Send call.
|
||||||
|
*
|
||||||
|
* @param user as in {@link DatagramPeerIO_SetHandlers}
|
||||||
|
*/
|
||||||
|
typedef void (*DatagramPeerIO_handler_error) (void *user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler function invoked when the number of used OTPs has reached
|
||||||
|
* the specified warning number in {@link DatagramPeerIO_SetOTPWarningHandler}.
|
||||||
|
* May be called from within a sending Send call.
|
||||||
|
*
|
||||||
|
* @param user as in {@link DatagramPeerIO_SetHandlers}
|
||||||
|
*/
|
||||||
|
typedef void (*DatagramPeerIO_handler_otp_warning) (void *user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler called when OTP generation for a new receive seed is finished.
|
||||||
|
*
|
||||||
|
* @param user as in {@link DatagramPeerIO_SetHandlers}
|
||||||
|
*/
|
||||||
|
typedef void (*DatagramPeerIO_handler_otp_ready) (void *user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object for comminicating with a peer using a datagram socket.
|
||||||
|
*
|
||||||
|
* The user provides data for sending to the peer through {@link PacketPassInterface}.
|
||||||
|
* Received data is provided to the user through {@link PacketPassInterface}.
|
||||||
|
*
|
||||||
|
* The object has a logical state called a mode, which is one of the following:
|
||||||
|
* - default - nothing is send or received
|
||||||
|
* - connecting - an address was provided by the user for sending datagrams to.
|
||||||
|
* Datagrams are being sent to that address through a socket,
|
||||||
|
* and datagrams are being received on the same socket.
|
||||||
|
* - binding - an address was provided by the user to bind a socket to.
|
||||||
|
* Datagrams are being received on the socket. Datagrams are not being
|
||||||
|
* sent initially. When a datagram is received, its source address is
|
||||||
|
* used as a destination address for sending datagrams.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
DebugObject d_obj;
|
||||||
|
BReactor *reactor;
|
||||||
|
int payload_mtu;
|
||||||
|
struct spproto_security_params sp_params;
|
||||||
|
void *user;
|
||||||
|
BLog_logfunc logfunc;
|
||||||
|
DatagramPeerIO_handler_error handler_error;
|
||||||
|
int spproto_payload_mtu;
|
||||||
|
int effective_socket_mtu;
|
||||||
|
|
||||||
|
// sending base
|
||||||
|
FragmentProtoDisassembler send_disassembler;
|
||||||
|
SPProtoEncoder send_encoder;
|
||||||
|
SinglePacketBuffer send_buffer;
|
||||||
|
PacketPassConnector send_connector;
|
||||||
|
|
||||||
|
// receiving
|
||||||
|
PacketRecvConnector recv_connector;
|
||||||
|
SinglePacketBuffer recv_buffer;
|
||||||
|
SPProtoDecoder recv_decoder;
|
||||||
|
PacketPassNotifier recv_notifier;
|
||||||
|
FragmentProtoAssembler recv_assembler;
|
||||||
|
|
||||||
|
// mode
|
||||||
|
int mode;
|
||||||
|
|
||||||
|
// datagram object
|
||||||
|
BDatagram dgram;
|
||||||
|
} DatagramPeerIO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the object.
|
||||||
|
* The interface is initialized in default mode.
|
||||||
|
* {@link BLog_Init} must have been done.
|
||||||
|
* {@link BNetwork_GlobalInit} must have been done.
|
||||||
|
* {@link BSecurity_GlobalInitThreadSafe} must have been done if
|
||||||
|
* {@link BThreadWorkDispatcher_UsingThreads}(twd) = 1.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param reactor {@link BReactor} we live in
|
||||||
|
* @param payload_mtu maximum payload size. Must be >=0.
|
||||||
|
* @param socket_mtu maximum datagram size for the socket. Must be >=0. Must be large enough so it is possible to
|
||||||
|
* send a FragmentProto chunk with one byte of data over SPProto, i.e. the following has to hold:
|
||||||
|
* spproto_payload_mtu_for_carrier_mtu(sp_params, socket_mtu) > sizeof(struct fragmentproto_chunk_header)
|
||||||
|
* @param sp_params SPProto security parameters
|
||||||
|
* @param latency latency parameter to {@link FragmentProtoDisassembler_Init}.
|
||||||
|
* @param num_frames num_frames parameter to {@link FragmentProtoAssembler_Init}. Must be >0.
|
||||||
|
* @param recv_userif interface to pass received packets to the user. Its MTU must be >=payload_mtu.
|
||||||
|
* @param otp_warning_count If using OTPs, after how many encoded packets to call the handler.
|
||||||
|
* In this case, must be >0 and <=sp_params.otp_num.
|
||||||
|
* @param twd thread work dispatcher
|
||||||
|
* @param user value to pass to handlers
|
||||||
|
* @param logfunc function which prepends the log prefix using {@link BLog_Append}
|
||||||
|
* @param handler_error error handler
|
||||||
|
* @param handler_otp_warning OTP warning handler
|
||||||
|
* @param handler_otp_ready handler called when OTP generation for a new receive seed is finished
|
||||||
|
* @return 1 on success, 0 on failure
|
||||||
|
*/
|
||||||
|
int DatagramPeerIO_Init (
|
||||||
|
DatagramPeerIO *o,
|
||||||
|
BReactor *reactor,
|
||||||
|
int payload_mtu,
|
||||||
|
int socket_mtu,
|
||||||
|
struct spproto_security_params sp_params,
|
||||||
|
btime_t latency,
|
||||||
|
int num_frames,
|
||||||
|
PacketPassInterface *recv_userif,
|
||||||
|
int otp_warning_count,
|
||||||
|
BThreadWorkDispatcher *twd,
|
||||||
|
void *user,
|
||||||
|
BLog_logfunc logfunc,
|
||||||
|
DatagramPeerIO_handler_error handler_error,
|
||||||
|
DatagramPeerIO_handler_otp_warning handler_otp_warning,
|
||||||
|
DatagramPeerIO_handler_otp_ready handler_otp_ready
|
||||||
|
) WARN_UNUSED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the object.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void DatagramPeerIO_Free (DatagramPeerIO *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an interface the user should use to send packets.
|
||||||
|
* The OTP warning handler may be called from within Send calls
|
||||||
|
* to the interface.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @return sending interface
|
||||||
|
*/
|
||||||
|
PacketPassInterface * DatagramPeerIO_GetSendInput (DatagramPeerIO *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to establish connection to the peer which has bound to an address.
|
||||||
|
* On success, the interface enters connecting mode.
|
||||||
|
* On failure, the interface enters default mode.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param addr address to send packets to
|
||||||
|
* @return 1 on success, 0 on failure
|
||||||
|
*/
|
||||||
|
int DatagramPeerIO_Connect (DatagramPeerIO *o, BAddr addr) WARN_UNUSED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to establish connection to the peer by binding to an address.
|
||||||
|
* On success, the interface enters connecting mode.
|
||||||
|
* On failure, the interface enters default mode.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param addr address to bind to. Must be supported according to
|
||||||
|
* {@link BDatagram_AddressFamilySupported}.
|
||||||
|
* @return 1 on success, 0 on failure
|
||||||
|
*/
|
||||||
|
int DatagramPeerIO_Bind (DatagramPeerIO *o, BAddr addr) WARN_UNUSED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the encryption key to use for sending and receiving.
|
||||||
|
* Encryption must be enabled.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param encryption_key key to use
|
||||||
|
*/
|
||||||
|
void DatagramPeerIO_SetEncryptionKey (DatagramPeerIO *o, uint8_t *encryption_key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removed the encryption key to use for sending and receiving.
|
||||||
|
* Encryption must be enabled.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void DatagramPeerIO_RemoveEncryptionKey (DatagramPeerIO *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the OTP seed for sending.
|
||||||
|
* OTPs must be enabled.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param seed_id seed identifier
|
||||||
|
* @param key OTP encryption key
|
||||||
|
* @param iv OTP initialization vector
|
||||||
|
*/
|
||||||
|
void DatagramPeerIO_SetOTPSendSeed (DatagramPeerIO *o, uint16_t seed_id, uint8_t *key, uint8_t *iv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the OTP seed for sending of one is configured.
|
||||||
|
* OTPs must be enabled.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void DatagramPeerIO_RemoveOTPSendSeed (DatagramPeerIO *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an OTP seed for reciving.
|
||||||
|
* OTPs must be enabled.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param seed_id seed identifier
|
||||||
|
* @param key OTP encryption key
|
||||||
|
* @param iv OTP initialization vector
|
||||||
|
*/
|
||||||
|
void DatagramPeerIO_AddOTPRecvSeed (DatagramPeerIO *o, uint16_t seed_id, uint8_t *key, uint8_t *iv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all OTP seeds for reciving.
|
||||||
|
* OTPs must be enabled.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void DatagramPeerIO_RemoveOTPRecvSeeds (DatagramPeerIO *o);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,469 @@
|
||||||
|
/**
|
||||||
|
* @file FragmentProtoAssembler.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <misc/offset.h>
|
||||||
|
#include <misc/byteorder.h>
|
||||||
|
#include <misc/balloc.h>
|
||||||
|
|
||||||
|
#include "FragmentProtoAssembler.h"
|
||||||
|
|
||||||
|
#include <generated/blog_channel_FragmentProtoAssembler.h>
|
||||||
|
|
||||||
|
#define PeerLog(_o, ...) BLog_LogViaFunc((_o)->logfunc, (_o)->user, BLOG_CURRENT_CHANNEL, __VA_ARGS__)
|
||||||
|
|
||||||
|
#include "FragmentProtoAssembler_tree.h"
|
||||||
|
#include <structure/SAvl_impl.h>
|
||||||
|
|
||||||
|
static void free_frame (FragmentProtoAssembler *o, struct FragmentProtoAssembler_frame *frame)
|
||||||
|
{
|
||||||
|
// remove from used list
|
||||||
|
LinkedList1_Remove(&o->frames_used, &frame->list_node);
|
||||||
|
// remove from used tree
|
||||||
|
FPAFramesTree_Remove(&o->frames_used_tree, 0, frame);
|
||||||
|
|
||||||
|
// append to free list
|
||||||
|
LinkedList1_Append(&o->frames_free, &frame->list_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_oldest_frame (FragmentProtoAssembler *o)
|
||||||
|
{
|
||||||
|
ASSERT(!LinkedList1_IsEmpty(&o->frames_used))
|
||||||
|
|
||||||
|
// obtain oldest frame (first on the list)
|
||||||
|
LinkedList1Node *list_node = LinkedList1_GetFirst(&o->frames_used);
|
||||||
|
ASSERT(list_node)
|
||||||
|
struct FragmentProtoAssembler_frame *frame = UPPER_OBJECT(list_node, struct FragmentProtoAssembler_frame, list_node);
|
||||||
|
|
||||||
|
// free frame
|
||||||
|
free_frame(o, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct FragmentProtoAssembler_frame * allocate_new_frame (FragmentProtoAssembler *o, fragmentproto_frameid id)
|
||||||
|
{
|
||||||
|
ASSERT(!FPAFramesTree_LookupExact(&o->frames_used_tree, 0, id))
|
||||||
|
|
||||||
|
// if there are no free entries, free the oldest used one
|
||||||
|
if (LinkedList1_IsEmpty(&o->frames_free)) {
|
||||||
|
PeerLog(o, BLOG_INFO, "freeing used frame");
|
||||||
|
free_oldest_frame(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
// obtain frame entry
|
||||||
|
LinkedList1Node *list_node = LinkedList1_GetFirst(&o->frames_free);
|
||||||
|
ASSERT(list_node)
|
||||||
|
struct FragmentProtoAssembler_frame *frame = UPPER_OBJECT(list_node, struct FragmentProtoAssembler_frame, list_node);
|
||||||
|
|
||||||
|
// remove from free list
|
||||||
|
LinkedList1_Remove(&o->frames_free, &frame->list_node);
|
||||||
|
|
||||||
|
// initialize values
|
||||||
|
frame->id = id;
|
||||||
|
frame->time = o->time;
|
||||||
|
frame->num_chunks = 0;
|
||||||
|
frame->sum = 0;
|
||||||
|
frame->length = -1;
|
||||||
|
frame->length_so_far = 0;
|
||||||
|
|
||||||
|
// append to used list
|
||||||
|
LinkedList1_Append(&o->frames_used, &frame->list_node);
|
||||||
|
// insert to used tree
|
||||||
|
int res = FPAFramesTree_Insert(&o->frames_used_tree, 0, frame, NULL);
|
||||||
|
ASSERT_EXECUTE(res)
|
||||||
|
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int chunks_overlap (int c1_start, int c1_len, int c2_start, int c2_len)
|
||||||
|
{
|
||||||
|
return (c1_start + c1_len > c2_start && c2_start + c2_len > c1_start);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int frame_is_timed_out (FragmentProtoAssembler *o, struct FragmentProtoAssembler_frame *frame)
|
||||||
|
{
|
||||||
|
ASSERT(frame->time <= o->time)
|
||||||
|
|
||||||
|
return (o->time - frame->time > o->time_tolerance);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reduce_times (FragmentProtoAssembler *o)
|
||||||
|
{
|
||||||
|
// find the frame with minimal time, removing timed out frames
|
||||||
|
struct FragmentProtoAssembler_frame *minframe = NULL;
|
||||||
|
LinkedList1Node *list_node = LinkedList1_GetFirst(&o->frames_used);
|
||||||
|
while (list_node) {
|
||||||
|
LinkedList1Node *next = LinkedList1Node_Next(list_node);
|
||||||
|
struct FragmentProtoAssembler_frame *frame = UPPER_OBJECT(list_node, struct FragmentProtoAssembler_frame, list_node);
|
||||||
|
if (frame_is_timed_out(o, frame)) {
|
||||||
|
PeerLog(o, BLOG_INFO, "freeing timed out frame (while reducing times)");
|
||||||
|
free_frame(o, frame);
|
||||||
|
} else {
|
||||||
|
if (!minframe || frame->time < minframe->time) {
|
||||||
|
minframe = frame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list_node = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!minframe) {
|
||||||
|
// have no frames, set packet time to zero
|
||||||
|
o->time = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t min_time = minframe->time;
|
||||||
|
|
||||||
|
// subtract minimal time from all frames
|
||||||
|
for (list_node = LinkedList1_GetFirst(&o->frames_used); list_node; list_node = LinkedList1Node_Next(list_node)) {
|
||||||
|
struct FragmentProtoAssembler_frame *frame = UPPER_OBJECT(list_node, struct FragmentProtoAssembler_frame, list_node);
|
||||||
|
frame->time -= min_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
// subtract minimal time from packet time
|
||||||
|
o->time -= min_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int process_chunk (FragmentProtoAssembler *o, fragmentproto_frameid frame_id, int chunk_start, int chunk_len, int is_last, uint8_t *payload)
|
||||||
|
{
|
||||||
|
ASSERT(chunk_start >= 0)
|
||||||
|
ASSERT(chunk_len >= 0)
|
||||||
|
ASSERT(is_last == 0 || is_last == 1)
|
||||||
|
|
||||||
|
// verify chunk
|
||||||
|
|
||||||
|
// check start
|
||||||
|
if (chunk_start > o->output_mtu) {
|
||||||
|
PeerLog(o, BLOG_INFO, "chunk starts outside");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check frame size bound
|
||||||
|
if (chunk_len > o->output_mtu - chunk_start) {
|
||||||
|
PeerLog(o, BLOG_INFO, "chunk ends outside");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate end
|
||||||
|
int chunk_end = chunk_start + chunk_len;
|
||||||
|
ASSERT(chunk_end >= 0)
|
||||||
|
ASSERT(chunk_end <= o->output_mtu)
|
||||||
|
|
||||||
|
// lookup frame
|
||||||
|
struct FragmentProtoAssembler_frame *frame = FPAFramesTree_LookupExact(&o->frames_used_tree, 0, frame_id);
|
||||||
|
if (!frame) {
|
||||||
|
// frame not found, add a new one
|
||||||
|
frame = allocate_new_frame(o, frame_id);
|
||||||
|
} else {
|
||||||
|
// have existing frame with that ID
|
||||||
|
// check frame time
|
||||||
|
if (frame_is_timed_out(o, frame)) {
|
||||||
|
// frame is timed out, remove it and use a new one
|
||||||
|
PeerLog(o, BLOG_INFO, "freeing timed out frame (while processing chunk)");
|
||||||
|
free_frame(o, frame);
|
||||||
|
frame = allocate_new_frame(o, frame_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(frame->num_chunks < o->num_chunks)
|
||||||
|
|
||||||
|
// check if the chunk overlaps with any existing chunks
|
||||||
|
for (int i = 0; i < frame->num_chunks; i++) {
|
||||||
|
struct FragmentProtoAssembler_chunk *chunk = &frame->chunks[i];
|
||||||
|
if (chunks_overlap(chunk->start, chunk->len, chunk_start, chunk_len)) {
|
||||||
|
PeerLog(o, BLOG_INFO, "chunk overlaps with existing chunk");
|
||||||
|
goto fail_frame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_last) {
|
||||||
|
// this chunk is marked as last
|
||||||
|
if (frame->length >= 0) {
|
||||||
|
PeerLog(o, BLOG_INFO, "got last chunk, but already have one");
|
||||||
|
goto fail_frame;
|
||||||
|
}
|
||||||
|
// check if frame size according to this packet is consistent
|
||||||
|
// with existing chunks
|
||||||
|
if (frame->length_so_far > chunk_end) {
|
||||||
|
PeerLog(o, BLOG_INFO, "got last chunk, but already have data over its bound");
|
||||||
|
goto fail_frame;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if we have length, chunk must be in its bound
|
||||||
|
if (frame->length >= 0) {
|
||||||
|
if (chunk_end > frame->length) {
|
||||||
|
PeerLog(o, BLOG_INFO, "chunk out of length bound");
|
||||||
|
goto fail_frame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// chunk is good, add it
|
||||||
|
|
||||||
|
// update frame time
|
||||||
|
frame->time = o->time;
|
||||||
|
|
||||||
|
// add chunk entry
|
||||||
|
struct FragmentProtoAssembler_chunk *chunk = &frame->chunks[frame->num_chunks];
|
||||||
|
chunk->start = chunk_start;
|
||||||
|
chunk->len = chunk_len;
|
||||||
|
frame->num_chunks++;
|
||||||
|
|
||||||
|
// update sum
|
||||||
|
frame->sum += chunk_len;
|
||||||
|
|
||||||
|
// update length
|
||||||
|
if (is_last) {
|
||||||
|
frame->length = chunk_end;
|
||||||
|
} else {
|
||||||
|
if (frame->length < 0) {
|
||||||
|
if (frame->length_so_far < chunk_end) {
|
||||||
|
frame->length_so_far = chunk_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy chunk payload to buffer
|
||||||
|
memcpy(frame->buffer + chunk_start, payload, chunk_len);
|
||||||
|
|
||||||
|
// is frame incomplete?
|
||||||
|
if (frame->length < 0 || frame->sum < frame->length) {
|
||||||
|
// if all chunks are used, fail it
|
||||||
|
if (frame->num_chunks == o->num_chunks) {
|
||||||
|
PeerLog(o, BLOG_INFO, "all chunks used, but frame not complete");
|
||||||
|
goto fail_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for more chunks
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(frame->sum == frame->length)
|
||||||
|
|
||||||
|
PeerLog(o, BLOG_DEBUG, "frame complete");
|
||||||
|
|
||||||
|
// free frame entry
|
||||||
|
free_frame(o, frame);
|
||||||
|
|
||||||
|
// send frame
|
||||||
|
PacketPassInterface_Sender_Send(o->output, frame->buffer, frame->length);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail_frame:
|
||||||
|
free_frame(o, frame);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void process_input (FragmentProtoAssembler *o)
|
||||||
|
{
|
||||||
|
ASSERT(o->in_len >= 0)
|
||||||
|
|
||||||
|
// read chunks
|
||||||
|
while (o->in_pos < o->in_len) {
|
||||||
|
// obtain header
|
||||||
|
if (o->in_len - o->in_pos < sizeof(struct fragmentproto_chunk_header)) {
|
||||||
|
PeerLog(o, BLOG_INFO, "too little data for chunk header");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
struct fragmentproto_chunk_header header;
|
||||||
|
memcpy(&header, o->in + o->in_pos, sizeof(header));
|
||||||
|
o->in_pos += sizeof(struct fragmentproto_chunk_header);
|
||||||
|
fragmentproto_frameid frame_id = ltoh16(header.frame_id);
|
||||||
|
int chunk_start = ltoh16(header.chunk_start);
|
||||||
|
int chunk_len = ltoh16(header.chunk_len);
|
||||||
|
int is_last = ltoh8(header.is_last);
|
||||||
|
|
||||||
|
// check is_last field
|
||||||
|
if (!(is_last == 0 || is_last == 1)) {
|
||||||
|
PeerLog(o, BLOG_INFO, "chunk is_last wrong");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// obtain data
|
||||||
|
if (o->in_len - o->in_pos < chunk_len) {
|
||||||
|
PeerLog(o, BLOG_INFO, "too little data for chunk data");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// process chunk
|
||||||
|
int res = process_chunk(o, frame_id, chunk_start, chunk_len, is_last, o->in + o->in_pos);
|
||||||
|
o->in_pos += chunk_len;
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
// sending complete frame, stop processing input
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// increment packet time
|
||||||
|
if (o->time == FPA_MAX_TIME) {
|
||||||
|
reduce_times(o);
|
||||||
|
if (!LinkedList1_IsEmpty(&o->frames_used)) {
|
||||||
|
ASSERT(o->time < FPA_MAX_TIME) // If there was a frame with zero time, it was removed because
|
||||||
|
// time_tolerance < FPA_MAX_TIME. So something >0 was subtracted.
|
||||||
|
o->time++;
|
||||||
|
} else {
|
||||||
|
// it was set to zero by reduce_times
|
||||||
|
ASSERT(o->time == 0)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
o->time++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set no input packet
|
||||||
|
o->in_len = -1;
|
||||||
|
|
||||||
|
// finish input
|
||||||
|
PacketPassInterface_Done(&o->input);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void input_handler_send (FragmentProtoAssembler *o, uint8_t *data, int data_len)
|
||||||
|
{
|
||||||
|
ASSERT(data_len >= 0)
|
||||||
|
ASSERT(o->in_len == -1)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// save input packet
|
||||||
|
o->in_len = data_len;
|
||||||
|
o->in = data;
|
||||||
|
o->in_pos = 0;
|
||||||
|
|
||||||
|
process_input(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_handler_done (FragmentProtoAssembler *o)
|
||||||
|
{
|
||||||
|
ASSERT(o->in_len >= 0)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
process_input(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FragmentProtoAssembler_Init (FragmentProtoAssembler *o, int input_mtu, PacketPassInterface *output, int num_frames, int num_chunks, BPendingGroup *pg, void *user, BLog_logfunc logfunc)
|
||||||
|
{
|
||||||
|
ASSERT(input_mtu >= 0)
|
||||||
|
ASSERT(num_frames > 0)
|
||||||
|
ASSERT(num_frames < FPA_MAX_TIME) // needed so we can always subtract times when packet time is maximum
|
||||||
|
ASSERT(num_chunks > 0)
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
o->output = output;
|
||||||
|
o->num_chunks = num_chunks;
|
||||||
|
o->user = user;
|
||||||
|
o->logfunc = logfunc;
|
||||||
|
|
||||||
|
// init input
|
||||||
|
PacketPassInterface_Init(&o->input, input_mtu, (PacketPassInterface_handler_send)input_handler_send, o, pg);
|
||||||
|
|
||||||
|
// init output
|
||||||
|
PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o);
|
||||||
|
|
||||||
|
// remebmer output MTU
|
||||||
|
o->output_mtu = PacketPassInterface_GetMTU(o->output);
|
||||||
|
|
||||||
|
// set packet time to zero
|
||||||
|
o->time = 0;
|
||||||
|
|
||||||
|
// set time tolerance to num_frames
|
||||||
|
o->time_tolerance = num_frames;
|
||||||
|
|
||||||
|
// allocate frames
|
||||||
|
if (!(o->frames_entries = (struct FragmentProtoAssembler_frame *)BAllocArray(num_frames, sizeof(o->frames_entries[0])))) {
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate chunks
|
||||||
|
if (!(o->frames_chunks = (struct FragmentProtoAssembler_chunk *)BAllocArray2(num_frames, o->num_chunks, sizeof(o->frames_chunks[0])))) {
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate buffers
|
||||||
|
if (!(o->frames_buffer = (uint8_t *)BAllocArray(num_frames, o->output_mtu))) {
|
||||||
|
goto fail3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init frame lists
|
||||||
|
LinkedList1_Init(&o->frames_free);
|
||||||
|
LinkedList1_Init(&o->frames_used);
|
||||||
|
|
||||||
|
// initialize frame entries
|
||||||
|
for (int i = 0; i < num_frames; i++) {
|
||||||
|
struct FragmentProtoAssembler_frame *frame = &o->frames_entries[i];
|
||||||
|
// set chunks array pointer
|
||||||
|
frame->chunks = o->frames_chunks + (size_t)i * o->num_chunks;
|
||||||
|
// set buffer pointer
|
||||||
|
frame->buffer = o->frames_buffer + (size_t)i * o->output_mtu;
|
||||||
|
// add to free list
|
||||||
|
LinkedList1_Append(&o->frames_free, &frame->list_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// init tree
|
||||||
|
FPAFramesTree_Init(&o->frames_used_tree);
|
||||||
|
|
||||||
|
// have no input packet
|
||||||
|
o->in_len = -1;
|
||||||
|
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail3:
|
||||||
|
BFree(o->frames_chunks);
|
||||||
|
fail2:
|
||||||
|
BFree(o->frames_entries);
|
||||||
|
fail1:
|
||||||
|
PacketPassInterface_Free(&o->input);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FragmentProtoAssembler_Free (FragmentProtoAssembler *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
|
||||||
|
// free buffers
|
||||||
|
BFree(o->frames_buffer);
|
||||||
|
|
||||||
|
// free chunks
|
||||||
|
BFree(o->frames_chunks);
|
||||||
|
|
||||||
|
// free frames
|
||||||
|
BFree(o->frames_entries);
|
||||||
|
|
||||||
|
// free input
|
||||||
|
PacketPassInterface_Free(&o->input);
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketPassInterface * FragmentProtoAssembler_GetInput (FragmentProtoAssembler *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
return &o->input;
|
||||||
|
}
|
|
@ -0,0 +1,134 @@
|
||||||
|
/**
|
||||||
|
* @file FragmentProtoAssembler.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @section DESCRIPTION
|
||||||
|
*
|
||||||
|
* Object which decodes packets according to FragmentProto.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_CLIENT_FRAGMENTPROTOASSEMBLER_H
|
||||||
|
#define BADVPN_CLIENT_FRAGMENTPROTOASSEMBLER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <protocol/fragmentproto.h>
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <misc/compare.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <base/BLog.h>
|
||||||
|
#include <structure/LinkedList1.h>
|
||||||
|
#include <structure/SAvl.h>
|
||||||
|
#include <flow/PacketPassInterface.h>
|
||||||
|
|
||||||
|
#define FPA_MAX_TIME UINT32_MAX
|
||||||
|
|
||||||
|
struct FragmentProtoAssembler_frame;
|
||||||
|
|
||||||
|
#include "FragmentProtoAssembler_tree.h"
|
||||||
|
#include <structure/SAvl_decl.h>
|
||||||
|
|
||||||
|
struct FragmentProtoAssembler_chunk {
|
||||||
|
int start;
|
||||||
|
int len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FragmentProtoAssembler_frame {
|
||||||
|
LinkedList1Node list_node; // node in free or used list
|
||||||
|
struct FragmentProtoAssembler_chunk *chunks; // array of chunks, up to num_chunks
|
||||||
|
uint8_t *buffer; // buffer with frame data, size output_mtu
|
||||||
|
// everything below only defined when frame entry is used
|
||||||
|
fragmentproto_frameid id; // frame identifier
|
||||||
|
uint32_t time; // packet time when the last chunk was received
|
||||||
|
FPAFramesTreeNode tree_node; // node fields in tree for searching frames by id
|
||||||
|
int num_chunks; // number of valid chunks
|
||||||
|
int sum; // sum of all chunks' lengths
|
||||||
|
int length; // length of the frame, or -1 if not yet known
|
||||||
|
int length_so_far; // if length=-1, current data set's upper bound
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object which decodes packets according to FragmentProto.
|
||||||
|
*
|
||||||
|
* Input is with {@link PacketPassInterface}.
|
||||||
|
* Output is with {@link PacketPassInterface}.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
void *user;
|
||||||
|
BLog_logfunc logfunc;
|
||||||
|
PacketPassInterface input;
|
||||||
|
PacketPassInterface *output;
|
||||||
|
int output_mtu;
|
||||||
|
int num_chunks;
|
||||||
|
uint32_t time;
|
||||||
|
int time_tolerance;
|
||||||
|
struct FragmentProtoAssembler_frame *frames_entries;
|
||||||
|
struct FragmentProtoAssembler_chunk *frames_chunks;
|
||||||
|
uint8_t *frames_buffer;
|
||||||
|
LinkedList1 frames_free;
|
||||||
|
LinkedList1 frames_used;
|
||||||
|
FPAFramesTree frames_used_tree;
|
||||||
|
int in_len;
|
||||||
|
uint8_t *in;
|
||||||
|
int in_pos;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} FragmentProtoAssembler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the object.
|
||||||
|
* {@link BLog_Init} must have been done.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param input_mtu maximum input packet size. Must be >=0.
|
||||||
|
* @param output output interface
|
||||||
|
* @param num_frames number of frames we can hold. Must be >0 and < FPA_MAX_TIME.
|
||||||
|
* To make the assembler tolerate out-of-order input of degree D, set to D+2.
|
||||||
|
* Here, D is the minimum size of a hypothetical buffer needed to order the input.
|
||||||
|
* @param num_chunks maximum number of chunks a frame can come in. Must be >0.
|
||||||
|
* @param pg pending group
|
||||||
|
* @param user argument to handlers
|
||||||
|
* @param logfunc function which prepends the log prefix using {@link BLog_Append}
|
||||||
|
* @return 1 on success, 0 on failure
|
||||||
|
*/
|
||||||
|
int FragmentProtoAssembler_Init (FragmentProtoAssembler *o, int input_mtu, PacketPassInterface *output, int num_frames, int num_chunks, BPendingGroup *pg, void *user, BLog_logfunc logfunc) WARN_UNUSED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the object.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void FragmentProtoAssembler_Free (FragmentProtoAssembler *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the input interface.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @return input interface
|
||||||
|
*/
|
||||||
|
PacketPassInterface * FragmentProtoAssembler_GetInput (FragmentProtoAssembler *o);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,9 @@
|
||||||
|
#define SAVL_PARAM_NAME FPAFramesTree
|
||||||
|
#define SAVL_PARAM_FEATURE_COUNTS 0
|
||||||
|
#define SAVL_PARAM_FEATURE_NOKEYS 0
|
||||||
|
#define SAVL_PARAM_TYPE_ENTRY struct FragmentProtoAssembler_frame
|
||||||
|
#define SAVL_PARAM_TYPE_KEY fragmentproto_frameid
|
||||||
|
#define SAVL_PARAM_TYPE_ARG int
|
||||||
|
#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) B_COMPARE((entry1)->id, (entry2)->id)
|
||||||
|
#define SAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) B_COMPARE((key1), (entry2)->id)
|
||||||
|
#define SAVL_PARAM_MEMBER_NODE tree_node
|
|
@ -0,0 +1,229 @@
|
||||||
|
/**
|
||||||
|
* @file FragmentProtoDisassembler.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <misc/byteorder.h>
|
||||||
|
#include <misc/minmax.h>
|
||||||
|
|
||||||
|
#include "client/FragmentProtoDisassembler.h"
|
||||||
|
|
||||||
|
static void write_chunks (FragmentProtoDisassembler *o)
|
||||||
|
{
|
||||||
|
#define IN_AVAIL (o->in_len - o->in_used)
|
||||||
|
#define OUT_AVAIL ((o->output_mtu - o->out_used) - (int)sizeof(struct fragmentproto_chunk_header))
|
||||||
|
|
||||||
|
ASSERT(o->in_len >= 0)
|
||||||
|
ASSERT(o->out)
|
||||||
|
ASSERT(OUT_AVAIL > 0)
|
||||||
|
|
||||||
|
// write chunks to output packet
|
||||||
|
do {
|
||||||
|
// calculate chunk length
|
||||||
|
int chunk_len = bmin_int(IN_AVAIL, OUT_AVAIL);
|
||||||
|
if (o->chunk_mtu > 0) {
|
||||||
|
chunk_len = bmin_int(chunk_len, o->chunk_mtu);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write chunk header
|
||||||
|
struct fragmentproto_chunk_header header;
|
||||||
|
header.frame_id = htol16(o->frame_id);
|
||||||
|
header.chunk_start = htol16(o->in_used);
|
||||||
|
header.chunk_len = htol16(chunk_len);
|
||||||
|
header.is_last = (chunk_len == IN_AVAIL);
|
||||||
|
memcpy(o->out + o->out_used, &header, sizeof(header));
|
||||||
|
|
||||||
|
// write chunk data
|
||||||
|
memcpy(o->out + o->out_used + sizeof(struct fragmentproto_chunk_header), o->in + o->in_used, chunk_len);
|
||||||
|
|
||||||
|
// increment pointers
|
||||||
|
o->in_used += chunk_len;
|
||||||
|
o->out_used += sizeof(struct fragmentproto_chunk_header) + chunk_len;
|
||||||
|
} while (IN_AVAIL > 0 && OUT_AVAIL > 0);
|
||||||
|
|
||||||
|
// have we finished the input packet?
|
||||||
|
if (IN_AVAIL == 0) {
|
||||||
|
// set no input packet
|
||||||
|
o->in_len = -1;
|
||||||
|
|
||||||
|
// increment frame ID
|
||||||
|
o->frame_id++;
|
||||||
|
|
||||||
|
// finish input
|
||||||
|
PacketPassInterface_Done(&o->input);
|
||||||
|
}
|
||||||
|
|
||||||
|
// should we finish the output packet?
|
||||||
|
if (OUT_AVAIL <= 0 || o->latency < 0) {
|
||||||
|
// set no output packet
|
||||||
|
o->out = NULL;
|
||||||
|
|
||||||
|
// stop timer (if it's running)
|
||||||
|
if (o->latency >= 0) {
|
||||||
|
BReactor_RemoveTimer(o->reactor, &o->timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// finish output
|
||||||
|
PacketRecvInterface_Done(&o->output, o->out_used);
|
||||||
|
} else {
|
||||||
|
// start timer if we have output and it's not running (output was empty before)
|
||||||
|
if (!BTimer_IsRunning(&o->timer)) {
|
||||||
|
BReactor_SetTimer(o->reactor, &o->timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void input_handler_send (FragmentProtoDisassembler *o, uint8_t *data, int data_len)
|
||||||
|
{
|
||||||
|
ASSERT(data_len >= 0)
|
||||||
|
ASSERT(o->in_len == -1)
|
||||||
|
|
||||||
|
// set input packet
|
||||||
|
o->in_len = data_len;
|
||||||
|
o->in = data;
|
||||||
|
o->in_used = 0;
|
||||||
|
|
||||||
|
// if there is no output, wait for it
|
||||||
|
if (!o->out) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_chunks(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void input_handler_requestcancel (FragmentProtoDisassembler *o)
|
||||||
|
{
|
||||||
|
ASSERT(o->in_len >= 0)
|
||||||
|
ASSERT(!o->out)
|
||||||
|
|
||||||
|
// set no input packet
|
||||||
|
o->in_len = -1;
|
||||||
|
|
||||||
|
// finish input
|
||||||
|
PacketPassInterface_Done(&o->input);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_handler_recv (FragmentProtoDisassembler *o, uint8_t *data)
|
||||||
|
{
|
||||||
|
ASSERT(data)
|
||||||
|
ASSERT(!o->out)
|
||||||
|
|
||||||
|
// set output packet
|
||||||
|
o->out = data;
|
||||||
|
o->out_used = 0;
|
||||||
|
|
||||||
|
// if there is no input, wait for it
|
||||||
|
if (o->in_len < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_chunks(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void timer_handler (FragmentProtoDisassembler *o)
|
||||||
|
{
|
||||||
|
ASSERT(o->latency >= 0)
|
||||||
|
ASSERT(o->out)
|
||||||
|
ASSERT(o->in_len == -1)
|
||||||
|
|
||||||
|
// set no output packet
|
||||||
|
o->out = NULL;
|
||||||
|
|
||||||
|
// finish output
|
||||||
|
PacketRecvInterface_Done(&o->output, o->out_used);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FragmentProtoDisassembler_Init (FragmentProtoDisassembler *o, BReactor *reactor, int input_mtu, int output_mtu, int chunk_mtu, btime_t latency)
|
||||||
|
{
|
||||||
|
ASSERT(input_mtu >= 0)
|
||||||
|
ASSERT(input_mtu <= UINT16_MAX)
|
||||||
|
ASSERT(output_mtu > sizeof(struct fragmentproto_chunk_header))
|
||||||
|
ASSERT(chunk_mtu > 0 || chunk_mtu < 0)
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
o->reactor = reactor;
|
||||||
|
o->output_mtu = output_mtu;
|
||||||
|
o->chunk_mtu = chunk_mtu;
|
||||||
|
o->latency = latency;
|
||||||
|
|
||||||
|
// init input
|
||||||
|
PacketPassInterface_Init(&o->input, input_mtu, (PacketPassInterface_handler_send)input_handler_send, o, BReactor_PendingGroup(reactor));
|
||||||
|
PacketPassInterface_EnableCancel(&o->input, (PacketPassInterface_handler_requestcancel)input_handler_requestcancel);
|
||||||
|
|
||||||
|
// init output
|
||||||
|
PacketRecvInterface_Init(&o->output, o->output_mtu, (PacketRecvInterface_handler_recv)output_handler_recv, o, BReactor_PendingGroup(reactor));
|
||||||
|
|
||||||
|
// init timer
|
||||||
|
if (o->latency >= 0) {
|
||||||
|
BTimer_Init(&o->timer, o->latency, (BTimer_handler)timer_handler, o);
|
||||||
|
}
|
||||||
|
|
||||||
|
// have no input packet
|
||||||
|
o->in_len = -1;
|
||||||
|
|
||||||
|
// have no output packet
|
||||||
|
o->out = NULL;
|
||||||
|
|
||||||
|
// start with zero frame ID
|
||||||
|
o->frame_id = 0;
|
||||||
|
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FragmentProtoDisassembler_Free (FragmentProtoDisassembler *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
|
||||||
|
// free timer
|
||||||
|
if (o->latency >= 0) {
|
||||||
|
BReactor_RemoveTimer(o->reactor, &o->timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// free output
|
||||||
|
PacketRecvInterface_Free(&o->output);
|
||||||
|
|
||||||
|
// free input
|
||||||
|
PacketPassInterface_Free(&o->input);
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketPassInterface * FragmentProtoDisassembler_GetInput (FragmentProtoDisassembler *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
return &o->input;
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketRecvInterface * FragmentProtoDisassembler_GetOutput (FragmentProtoDisassembler *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
return &o->output;
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
/**
|
||||||
|
* @file FragmentProtoDisassembler.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @section DESCRIPTION
|
||||||
|
*
|
||||||
|
* Object which encodes packets into packets composed of chunks
|
||||||
|
* according to FragmentProto.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_CLIENT_CCPROTODISASSEMBLER_H
|
||||||
|
#define BADVPN_CLIENT_CCPROTODISASSEMBLER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <protocol/fragmentproto.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <system/BReactor.h>
|
||||||
|
#include <system/BTime.h>
|
||||||
|
#include <flow/PacketPassInterface.h>
|
||||||
|
#include <flow/PacketRecvInterface.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object which encodes packets into packets composed of chunks
|
||||||
|
* according to FragmentProto.
|
||||||
|
*
|
||||||
|
* Input is with {@link PacketPassInterface}.
|
||||||
|
* Output is with {@link PacketRecvInterface}.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
BReactor *reactor;
|
||||||
|
int output_mtu;
|
||||||
|
int chunk_mtu;
|
||||||
|
btime_t latency;
|
||||||
|
PacketPassInterface input;
|
||||||
|
PacketRecvInterface output;
|
||||||
|
BTimer timer;
|
||||||
|
int in_len;
|
||||||
|
uint8_t *in;
|
||||||
|
int in_used;
|
||||||
|
uint8_t *out;
|
||||||
|
int out_used;
|
||||||
|
fragmentproto_frameid frame_id;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} FragmentProtoDisassembler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the object.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param reactor reactor we live in
|
||||||
|
* @param input_mtu maximum input packet size. Must be >=0 and <=UINT16_MAX.
|
||||||
|
* @param output_mtu maximum output packet size. Must be >sizeof(struct fragmentproto_chunk_header).
|
||||||
|
* @param chunk_mtu maximum chunk size. Must be >0, or <0 for no explicit limit.
|
||||||
|
* @param latency maximum time a pending output packet with some data can wait for more data
|
||||||
|
* before being sent out. If nonnegative, a timer will be used. If negative,
|
||||||
|
* packets will always be sent out immediately. If low latency is desired,
|
||||||
|
* prefer setting this to zero rather than negative.
|
||||||
|
*/
|
||||||
|
void FragmentProtoDisassembler_Init (FragmentProtoDisassembler *o, BReactor *reactor, int input_mtu, int output_mtu, int chunk_mtu, btime_t latency);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the object.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void FragmentProtoDisassembler_Free (FragmentProtoDisassembler *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the input interface.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @return input interface
|
||||||
|
*/
|
||||||
|
PacketPassInterface * FragmentProtoDisassembler_GetInput (FragmentProtoDisassembler *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the output interface.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @return output interface
|
||||||
|
*/
|
||||||
|
PacketRecvInterface * FragmentProtoDisassembler_GetOutput (FragmentProtoDisassembler *o);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,795 @@
|
||||||
|
/**
|
||||||
|
* @file FrameDecider.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <misc/offset.h>
|
||||||
|
#include <misc/balloc.h>
|
||||||
|
#include <misc/ethernet_proto.h>
|
||||||
|
#include <misc/ipv4_proto.h>
|
||||||
|
#include <misc/igmp_proto.h>
|
||||||
|
#include <misc/byteorder.h>
|
||||||
|
#include <misc/compare.h>
|
||||||
|
#include <misc/print_macros.h>
|
||||||
|
|
||||||
|
#include <client/FrameDecider.h>
|
||||||
|
|
||||||
|
#include <generated/blog_channel_FrameDecider.h>
|
||||||
|
|
||||||
|
#define DECIDE_STATE_NONE 1
|
||||||
|
#define DECIDE_STATE_UNICAST 2
|
||||||
|
#define DECIDE_STATE_FLOOD 3
|
||||||
|
#define DECIDE_STATE_MULTICAST 4
|
||||||
|
|
||||||
|
#define PeerLog(_o, ...) BLog_LogViaFunc((_o)->logfunc, (_o)->user, BLOG_CURRENT_CHANNEL, __VA_ARGS__)
|
||||||
|
|
||||||
|
static int compare_macs (const uint8_t *mac1, const uint8_t *mac2)
|
||||||
|
{
|
||||||
|
int c = memcmp(mac1, mac2, 6);
|
||||||
|
return B_COMPARE(c, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "FrameDecider_macs_tree.h"
|
||||||
|
#include <structure/SAvl_impl.h>
|
||||||
|
|
||||||
|
#include "FrameDecider_groups_tree.h"
|
||||||
|
#include <structure/SAvl_impl.h>
|
||||||
|
|
||||||
|
#include "FrameDecider_multicast_tree.h"
|
||||||
|
#include <structure/SAvl_impl.h>
|
||||||
|
|
||||||
|
static void add_mac_to_peer (FrameDeciderPeer *o, uint8_t *mac)
|
||||||
|
{
|
||||||
|
FrameDecider *d = o->d;
|
||||||
|
|
||||||
|
// locate entry in tree
|
||||||
|
struct _FrameDecider_mac_entry *e_entry = FDMacsTree_LookupExact(&d->macs_tree, 0, mac);
|
||||||
|
if (e_entry) {
|
||||||
|
if (e_entry->peer == o) {
|
||||||
|
// this is our MAC; only move it to the end of the used list
|
||||||
|
LinkedList1_Remove(&o->mac_entries_used, &e_entry->list_node);
|
||||||
|
LinkedList1_Append(&o->mac_entries_used, &e_entry->list_node);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// some other peer has that MAC; disassociate it
|
||||||
|
FDMacsTree_Remove(&d->macs_tree, 0, e_entry);
|
||||||
|
LinkedList1_Remove(&e_entry->peer->mac_entries_used, &e_entry->list_node);
|
||||||
|
LinkedList1_Append(&e_entry->peer->mac_entries_free, &e_entry->list_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// aquire MAC address entry, if there are no free ones reuse the oldest used one
|
||||||
|
LinkedList1Node *list_node;
|
||||||
|
struct _FrameDecider_mac_entry *entry;
|
||||||
|
if (list_node = LinkedList1_GetFirst(&o->mac_entries_free)) {
|
||||||
|
entry = UPPER_OBJECT(list_node, struct _FrameDecider_mac_entry, list_node);
|
||||||
|
ASSERT(entry->peer == o)
|
||||||
|
|
||||||
|
// remove from free
|
||||||
|
LinkedList1_Remove(&o->mac_entries_free, &entry->list_node);
|
||||||
|
} else {
|
||||||
|
list_node = LinkedList1_GetFirst(&o->mac_entries_used);
|
||||||
|
ASSERT(list_node)
|
||||||
|
entry = UPPER_OBJECT(list_node, struct _FrameDecider_mac_entry, list_node);
|
||||||
|
ASSERT(entry->peer == o)
|
||||||
|
|
||||||
|
// remove from used
|
||||||
|
FDMacsTree_Remove(&d->macs_tree, 0, entry);
|
||||||
|
LinkedList1_Remove(&o->mac_entries_used, &entry->list_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
PeerLog(o, BLOG_INFO, "adding MAC %02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8"", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||||
|
|
||||||
|
// set MAC in entry
|
||||||
|
memcpy(entry->mac, mac, sizeof(entry->mac));
|
||||||
|
|
||||||
|
// add to used
|
||||||
|
LinkedList1_Append(&o->mac_entries_used, &entry->list_node);
|
||||||
|
int res = FDMacsTree_Insert(&d->macs_tree, 0, entry, NULL);
|
||||||
|
ASSERT_EXECUTE(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t compute_sig_for_group (uint32_t group)
|
||||||
|
{
|
||||||
|
return hton32(ntoh32(group)&0x7FFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t compute_sig_for_mac (uint8_t *mac)
|
||||||
|
{
|
||||||
|
uint32_t sig;
|
||||||
|
memcpy(&sig, mac + 2, 4);
|
||||||
|
sig = hton32(ntoh32(sig)&0x7FFFFF);
|
||||||
|
return sig;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_to_multicast (FrameDecider *d, struct _FrameDecider_group_entry *group_entry)
|
||||||
|
{
|
||||||
|
// compute sig
|
||||||
|
uint32_t sig = compute_sig_for_group(group_entry->group);
|
||||||
|
|
||||||
|
struct _FrameDecider_group_entry *master = FDMulticastTree_LookupExact(&d->multicast_tree, 0, sig);
|
||||||
|
if (master) {
|
||||||
|
// use existing master
|
||||||
|
ASSERT(master->is_master)
|
||||||
|
|
||||||
|
// set not master
|
||||||
|
group_entry->is_master = 0;
|
||||||
|
|
||||||
|
// insert to list
|
||||||
|
LinkedList3Node_InitAfter(&group_entry->sig_list_node, &master->sig_list_node);
|
||||||
|
} else {
|
||||||
|
// make this entry master
|
||||||
|
|
||||||
|
// set master
|
||||||
|
group_entry->is_master = 1;
|
||||||
|
|
||||||
|
// set sig
|
||||||
|
group_entry->master.sig = sig;
|
||||||
|
|
||||||
|
// insert to multicast tree
|
||||||
|
int res = FDMulticastTree_Insert(&d->multicast_tree, 0, group_entry, NULL);
|
||||||
|
ASSERT_EXECUTE(res)
|
||||||
|
|
||||||
|
// init list node
|
||||||
|
LinkedList3Node_InitLonely(&group_entry->sig_list_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_from_multicast (FrameDecider *d, struct _FrameDecider_group_entry *group_entry)
|
||||||
|
{
|
||||||
|
// compute sig
|
||||||
|
uint32_t sig = compute_sig_for_group(group_entry->group);
|
||||||
|
|
||||||
|
if (group_entry->is_master) {
|
||||||
|
// remove master from multicast tree
|
||||||
|
FDMulticastTree_Remove(&d->multicast_tree, 0, group_entry);
|
||||||
|
|
||||||
|
if (!LinkedList3Node_IsLonely(&group_entry->sig_list_node)) {
|
||||||
|
// at least one more group entry for this sig; make another entry the master
|
||||||
|
|
||||||
|
// get an entry
|
||||||
|
LinkedList3Node *list_node = LinkedList3Node_NextOrPrev(&group_entry->sig_list_node);
|
||||||
|
struct _FrameDecider_group_entry *newmaster = UPPER_OBJECT(list_node, struct _FrameDecider_group_entry, sig_list_node);
|
||||||
|
ASSERT(!newmaster->is_master)
|
||||||
|
|
||||||
|
// set master
|
||||||
|
newmaster->is_master = 1;
|
||||||
|
|
||||||
|
// set sig
|
||||||
|
newmaster->master.sig = sig;
|
||||||
|
|
||||||
|
// insert to multicast tree
|
||||||
|
int res = FDMulticastTree_Insert(&d->multicast_tree, 0, newmaster, NULL);
|
||||||
|
ASSERT_EXECUTE(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// free linked list node
|
||||||
|
LinkedList3Node_Free(&group_entry->sig_list_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_group_to_peer (FrameDeciderPeer *o, uint32_t group)
|
||||||
|
{
|
||||||
|
FrameDecider *d = o->d;
|
||||||
|
|
||||||
|
struct _FrameDecider_group_entry *group_entry = FDGroupsTree_LookupExact(&o->groups_tree, 0, group);
|
||||||
|
if (group_entry) {
|
||||||
|
// move to end of used list
|
||||||
|
LinkedList1_Remove(&o->group_entries_used, &group_entry->list_node);
|
||||||
|
LinkedList1_Append(&o->group_entries_used, &group_entry->list_node);
|
||||||
|
} else {
|
||||||
|
PeerLog(o, BLOG_INFO, "joined group %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"",
|
||||||
|
((uint8_t *)&group)[0], ((uint8_t *)&group)[1], ((uint8_t *)&group)[2], ((uint8_t *)&group)[3]
|
||||||
|
);
|
||||||
|
|
||||||
|
// aquire group entry, if there are no free ones reuse the earliest used one
|
||||||
|
LinkedList1Node *node;
|
||||||
|
if (node = LinkedList1_GetFirst(&o->group_entries_free)) {
|
||||||
|
group_entry = UPPER_OBJECT(node, struct _FrameDecider_group_entry, list_node);
|
||||||
|
|
||||||
|
// remove from free list
|
||||||
|
LinkedList1_Remove(&o->group_entries_free, &group_entry->list_node);
|
||||||
|
} else {
|
||||||
|
node = LinkedList1_GetFirst(&o->group_entries_used);
|
||||||
|
ASSERT(node)
|
||||||
|
group_entry = UPPER_OBJECT(node, struct _FrameDecider_group_entry, list_node);
|
||||||
|
|
||||||
|
// remove from multicast
|
||||||
|
remove_from_multicast(d, group_entry);
|
||||||
|
|
||||||
|
// remove from peer's groups tree
|
||||||
|
FDGroupsTree_Remove(&o->groups_tree, 0, group_entry);
|
||||||
|
|
||||||
|
// remove from used list
|
||||||
|
LinkedList1_Remove(&o->group_entries_used, &group_entry->list_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add entry to used list
|
||||||
|
LinkedList1_Append(&o->group_entries_used, &group_entry->list_node);
|
||||||
|
|
||||||
|
// set group address
|
||||||
|
group_entry->group = group;
|
||||||
|
|
||||||
|
// insert to peer's groups tree
|
||||||
|
int res = FDGroupsTree_Insert(&o->groups_tree, 0, group_entry, NULL);
|
||||||
|
ASSERT_EXECUTE(res)
|
||||||
|
|
||||||
|
// add to multicast
|
||||||
|
add_to_multicast(d, group_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set timer
|
||||||
|
group_entry->timer_endtime = btime_gettime() + d->igmp_group_membership_interval;
|
||||||
|
BReactor_SetTimerAbsolute(d->reactor, &group_entry->timer, group_entry->timer_endtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_group_entry (struct _FrameDecider_group_entry *group_entry)
|
||||||
|
{
|
||||||
|
FrameDeciderPeer *peer = group_entry->peer;
|
||||||
|
FrameDecider *d = peer->d;
|
||||||
|
|
||||||
|
uint32_t group = group_entry->group;
|
||||||
|
|
||||||
|
PeerLog(peer, BLOG_INFO, "left group %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"",
|
||||||
|
((uint8_t *)&group)[0], ((uint8_t *)&group)[1], ((uint8_t *)&group)[2], ((uint8_t *)&group)[3]
|
||||||
|
);
|
||||||
|
|
||||||
|
// remove from multicast
|
||||||
|
remove_from_multicast(d, group_entry);
|
||||||
|
|
||||||
|
// remove from peer's groups tree
|
||||||
|
FDGroupsTree_Remove(&peer->groups_tree, 0, group_entry);
|
||||||
|
|
||||||
|
// remove from used list
|
||||||
|
LinkedList1_Remove(&peer->group_entries_used, &group_entry->list_node);
|
||||||
|
|
||||||
|
// add to free list
|
||||||
|
LinkedList1_Append(&peer->group_entries_free, &group_entry->list_node);
|
||||||
|
|
||||||
|
// stop timer
|
||||||
|
BReactor_RemoveTimer(d->reactor, &group_entry->timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lower_group_timers_to_lmqt (FrameDecider *d, uint32_t group)
|
||||||
|
{
|
||||||
|
// have to lower all the group timers of this group down to LMQT
|
||||||
|
|
||||||
|
// compute sig
|
||||||
|
uint32_t sig = compute_sig_for_group(group);
|
||||||
|
|
||||||
|
// look up the sig in multicast tree
|
||||||
|
struct _FrameDecider_group_entry *master = FDMulticastTree_LookupExact(&d->multicast_tree, 0, sig);
|
||||||
|
if (!master) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ASSERT(master->is_master)
|
||||||
|
|
||||||
|
// iterate all group entries with this sig
|
||||||
|
LinkedList3Iterator it;
|
||||||
|
LinkedList3Iterator_Init(&it, LinkedList3Node_First(&master->sig_list_node), 1);
|
||||||
|
LinkedList3Node *sig_list_node;
|
||||||
|
while (sig_list_node = LinkedList3Iterator_Next(&it)) {
|
||||||
|
struct _FrameDecider_group_entry *group_entry = UPPER_OBJECT(sig_list_node, struct _FrameDecider_group_entry, sig_list_node);
|
||||||
|
|
||||||
|
// skip wrong groups
|
||||||
|
if (group_entry->group != group) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lower timer down to LMQT
|
||||||
|
btime_t now = btime_gettime();
|
||||||
|
if (group_entry->timer_endtime > now + d->igmp_last_member_query_time) {
|
||||||
|
group_entry->timer_endtime = now + d->igmp_last_member_query_time;
|
||||||
|
BReactor_SetTimerAbsolute(d->reactor, &group_entry->timer, group_entry->timer_endtime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void group_entry_timer_handler (struct _FrameDecider_group_entry *group_entry)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&group_entry->peer->d_obj);
|
||||||
|
|
||||||
|
remove_group_entry(group_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameDecider_Init (FrameDecider *o, int max_peer_macs, int max_peer_groups, btime_t igmp_group_membership_interval, btime_t igmp_last_member_query_time, BReactor *reactor)
|
||||||
|
{
|
||||||
|
ASSERT(max_peer_macs > 0)
|
||||||
|
ASSERT(max_peer_groups > 0)
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
o->max_peer_macs = max_peer_macs;
|
||||||
|
o->max_peer_groups = max_peer_groups;
|
||||||
|
o->igmp_group_membership_interval = igmp_group_membership_interval;
|
||||||
|
o->igmp_last_member_query_time = igmp_last_member_query_time;
|
||||||
|
o->reactor = reactor;
|
||||||
|
|
||||||
|
// init peers list
|
||||||
|
LinkedList1_Init(&o->peers_list);
|
||||||
|
|
||||||
|
// init MAC tree
|
||||||
|
FDMacsTree_Init(&o->macs_tree);
|
||||||
|
|
||||||
|
// init multicast tree
|
||||||
|
FDMulticastTree_Init(&o->multicast_tree);
|
||||||
|
|
||||||
|
// init decide state
|
||||||
|
o->decide_state = DECIDE_STATE_NONE;
|
||||||
|
|
||||||
|
// set no current flood peer
|
||||||
|
o->decide_flood_current = NULL;
|
||||||
|
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameDecider_Free (FrameDecider *o)
|
||||||
|
{
|
||||||
|
ASSERT(FDMulticastTree_IsEmpty(&o->multicast_tree))
|
||||||
|
ASSERT(FDMacsTree_IsEmpty(&o->macs_tree))
|
||||||
|
ASSERT(LinkedList1_IsEmpty(&o->peers_list))
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameDecider_AnalyzeAndDecide (FrameDecider *o, const uint8_t *frame, int frame_len)
|
||||||
|
{
|
||||||
|
ASSERT(frame_len >= 0)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// reset decide state
|
||||||
|
switch (o->decide_state) {
|
||||||
|
case DECIDE_STATE_NONE:
|
||||||
|
break;
|
||||||
|
case DECIDE_STATE_UNICAST:
|
||||||
|
break;
|
||||||
|
case DECIDE_STATE_FLOOD:
|
||||||
|
break;
|
||||||
|
case DECIDE_STATE_MULTICAST:
|
||||||
|
LinkedList3Iterator_Free(&o->decide_multicast_it);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
ASSERT(0);
|
||||||
|
}
|
||||||
|
o->decide_state = DECIDE_STATE_NONE;
|
||||||
|
o->decide_flood_current = NULL;
|
||||||
|
|
||||||
|
// analyze frame
|
||||||
|
|
||||||
|
const uint8_t *pos = frame;
|
||||||
|
int len = frame_len;
|
||||||
|
|
||||||
|
if (len < sizeof(struct ethernet_header)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct ethernet_header eh;
|
||||||
|
memcpy(&eh, pos, sizeof(eh));
|
||||||
|
pos += sizeof(struct ethernet_header);
|
||||||
|
len -= sizeof(struct ethernet_header);
|
||||||
|
|
||||||
|
int is_igmp = 0;
|
||||||
|
|
||||||
|
switch (ntoh16(eh.type)) {
|
||||||
|
case ETHERTYPE_IPV4: {
|
||||||
|
// check IPv4 header
|
||||||
|
struct ipv4_header ipv4_header;
|
||||||
|
if (!ipv4_check((uint8_t *)pos, len, &ipv4_header, (uint8_t **)&pos, &len)) {
|
||||||
|
BLog(BLOG_INFO, "decide: wrong IP packet");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if it's IGMP
|
||||||
|
if (ntoh8(ipv4_header.protocol) != IPV4_PROTOCOL_IGMP) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remember that it's IGMP; we have to flood IGMP frames
|
||||||
|
is_igmp = 1;
|
||||||
|
|
||||||
|
// check IGMP header
|
||||||
|
if (len < sizeof(struct igmp_base)) {
|
||||||
|
BLog(BLOG_INFO, "decide: IGMP: short packet");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
struct igmp_base igmp_base;
|
||||||
|
memcpy(&igmp_base, pos, sizeof(igmp_base));
|
||||||
|
pos += sizeof(struct igmp_base);
|
||||||
|
len -= sizeof(struct igmp_base);
|
||||||
|
|
||||||
|
switch (ntoh8(igmp_base.type)) {
|
||||||
|
case IGMP_TYPE_MEMBERSHIP_QUERY: {
|
||||||
|
if (len == sizeof(struct igmp_v2_extra) && ntoh8(igmp_base.max_resp_code) != 0) {
|
||||||
|
// V2 query
|
||||||
|
struct igmp_v2_extra query;
|
||||||
|
memcpy(&query, pos, sizeof(query));
|
||||||
|
pos += sizeof(struct igmp_v2_extra);
|
||||||
|
len -= sizeof(struct igmp_v2_extra);
|
||||||
|
|
||||||
|
if (ntoh32(query.group) != 0) {
|
||||||
|
// got a Group-Specific Query, lower group timers to LMQT
|
||||||
|
lower_group_timers_to_lmqt(o, query.group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (len >= sizeof(struct igmp_v3_query_extra)) {
|
||||||
|
// V3 query
|
||||||
|
struct igmp_v3_query_extra query;
|
||||||
|
memcpy(&query, pos, sizeof(query));
|
||||||
|
pos += sizeof(struct igmp_v3_query_extra);
|
||||||
|
len -= sizeof(struct igmp_v3_query_extra);
|
||||||
|
|
||||||
|
// iterate sources
|
||||||
|
uint16_t num_sources = ntoh16(query.number_of_sources);
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < num_sources; i++) {
|
||||||
|
// check source
|
||||||
|
if (len < sizeof(struct igmp_source)) {
|
||||||
|
BLog(BLOG_NOTICE, "decide: IGMP: short source");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
pos += sizeof(struct igmp_source);
|
||||||
|
len -= sizeof(struct igmp_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ntoh32(query.group) != 0 && num_sources == 0) {
|
||||||
|
// got a Group-Specific Query, lower group timers to LMQT
|
||||||
|
lower_group_timers_to_lmqt(o, query.group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:;
|
||||||
|
|
||||||
|
const uint8_t broadcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
||||||
|
const uint8_t multicast_mac_header[] = {0x01, 0x00, 0x5e};
|
||||||
|
|
||||||
|
// if it's broadcast or IGMP, flood it
|
||||||
|
if (is_igmp || !memcmp(eh.dest, broadcast_mac, sizeof(broadcast_mac))) {
|
||||||
|
o->decide_state = DECIDE_STATE_FLOOD;
|
||||||
|
o->decide_flood_current = LinkedList1_GetFirst(&o->peers_list);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if it's multicast, forward to all peers with the given sig
|
||||||
|
if (!memcmp(eh.dest, multicast_mac_header, sizeof(multicast_mac_header))) {
|
||||||
|
// extract group's sig from destination MAC
|
||||||
|
uint32_t sig = compute_sig_for_mac(eh.dest);
|
||||||
|
|
||||||
|
// look up the sig in multicast tree
|
||||||
|
struct _FrameDecider_group_entry *master = FDMulticastTree_LookupExact(&o->multicast_tree, 0, sig);
|
||||||
|
if (master) {
|
||||||
|
ASSERT(master->is_master)
|
||||||
|
|
||||||
|
o->decide_state = DECIDE_STATE_MULTICAST;
|
||||||
|
LinkedList3Iterator_Init(&o->decide_multicast_it, LinkedList3Node_First(&master->sig_list_node), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// look for MAC entry
|
||||||
|
struct _FrameDecider_mac_entry *entry = FDMacsTree_LookupExact(&o->macs_tree, 0, eh.dest);
|
||||||
|
if (entry) {
|
||||||
|
o->decide_state = DECIDE_STATE_UNICAST;
|
||||||
|
o->decide_unicast_peer = entry->peer;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// unknown destination MAC, flood
|
||||||
|
o->decide_state = DECIDE_STATE_FLOOD;
|
||||||
|
o->decide_flood_current = LinkedList1_GetFirst(&o->peers_list);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameDeciderPeer * FrameDecider_NextDestination (FrameDecider *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
switch (o->decide_state) {
|
||||||
|
case DECIDE_STATE_NONE: {
|
||||||
|
return NULL;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DECIDE_STATE_UNICAST: {
|
||||||
|
o->decide_state = DECIDE_STATE_NONE;
|
||||||
|
|
||||||
|
return o->decide_unicast_peer;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DECIDE_STATE_FLOOD: {
|
||||||
|
if (!o->decide_flood_current) {
|
||||||
|
o->decide_state = DECIDE_STATE_NONE;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkedList1Node *list_node = o->decide_flood_current;
|
||||||
|
o->decide_flood_current = LinkedList1Node_Next(o->decide_flood_current);
|
||||||
|
|
||||||
|
FrameDeciderPeer *peer = UPPER_OBJECT(list_node, FrameDeciderPeer, list_node);
|
||||||
|
|
||||||
|
return peer;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DECIDE_STATE_MULTICAST: {
|
||||||
|
LinkedList3Node *list_node = LinkedList3Iterator_Next(&o->decide_multicast_it);
|
||||||
|
if (!list_node) {
|
||||||
|
o->decide_state = DECIDE_STATE_NONE;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
struct _FrameDecider_group_entry *group_entry = UPPER_OBJECT(list_node, struct _FrameDecider_group_entry, sig_list_node);
|
||||||
|
|
||||||
|
return group_entry->peer;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ASSERT(0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int FrameDeciderPeer_Init (FrameDeciderPeer *o, FrameDecider *d, void *user, BLog_logfunc logfunc)
|
||||||
|
{
|
||||||
|
// init arguments
|
||||||
|
o->d = d;
|
||||||
|
o->user = user;
|
||||||
|
o->logfunc = logfunc;
|
||||||
|
|
||||||
|
// allocate MAC entries
|
||||||
|
if (!(o->mac_entries = (struct _FrameDecider_mac_entry *)BAllocArray(d->max_peer_macs, sizeof(struct _FrameDecider_mac_entry)))) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "failed to allocate MAC entries");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate group entries
|
||||||
|
if (!(o->group_entries = (struct _FrameDecider_group_entry *)BAllocArray(d->max_peer_groups, sizeof(struct _FrameDecider_group_entry)))) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "failed to allocate group entries");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert to peers list
|
||||||
|
LinkedList1_Append(&d->peers_list, &o->list_node);
|
||||||
|
|
||||||
|
// init MAC entry lists
|
||||||
|
LinkedList1_Init(&o->mac_entries_free);
|
||||||
|
LinkedList1_Init(&o->mac_entries_used);
|
||||||
|
|
||||||
|
// initialize MAC entries
|
||||||
|
for (int i = 0; i < d->max_peer_macs; i++) {
|
||||||
|
struct _FrameDecider_mac_entry *entry = &o->mac_entries[i];
|
||||||
|
|
||||||
|
// set peer
|
||||||
|
entry->peer = o;
|
||||||
|
|
||||||
|
// insert to free list
|
||||||
|
LinkedList1_Append(&o->mac_entries_free, &entry->list_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// init group entry lists
|
||||||
|
LinkedList1_Init(&o->group_entries_free);
|
||||||
|
LinkedList1_Init(&o->group_entries_used);
|
||||||
|
|
||||||
|
// initialize group entries
|
||||||
|
for (int i = 0; i < d->max_peer_groups; i++) {
|
||||||
|
struct _FrameDecider_group_entry *entry = &o->group_entries[i];
|
||||||
|
|
||||||
|
// set peer
|
||||||
|
entry->peer = o;
|
||||||
|
|
||||||
|
// insert to free list
|
||||||
|
LinkedList1_Append(&o->group_entries_free, &entry->list_node);
|
||||||
|
|
||||||
|
// init timer
|
||||||
|
BTimer_Init(&entry->timer, 0, (BTimer_handler)group_entry_timer_handler, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize groups tree
|
||||||
|
FDGroupsTree_Init(&o->groups_tree);
|
||||||
|
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail1:
|
||||||
|
BFree(o->mac_entries);
|
||||||
|
fail0:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameDeciderPeer_Free (FrameDeciderPeer *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
|
||||||
|
FrameDecider *d = o->d;
|
||||||
|
|
||||||
|
// remove decide unicast reference
|
||||||
|
if (d->decide_state == DECIDE_STATE_UNICAST && d->decide_unicast_peer == o) {
|
||||||
|
d->decide_state = DECIDE_STATE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkedList1Node *node;
|
||||||
|
|
||||||
|
// free group entries
|
||||||
|
for (node = LinkedList1_GetFirst(&o->group_entries_used); node; node = LinkedList1Node_Next(node)) {
|
||||||
|
struct _FrameDecider_group_entry *entry = UPPER_OBJECT(node, struct _FrameDecider_group_entry, list_node);
|
||||||
|
|
||||||
|
// remove from multicast
|
||||||
|
remove_from_multicast(d, entry);
|
||||||
|
|
||||||
|
// stop timer
|
||||||
|
BReactor_RemoveTimer(d->reactor, &entry->timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove used MAC entries from tree
|
||||||
|
for (node = LinkedList1_GetFirst(&o->mac_entries_used); node; node = LinkedList1Node_Next(node)) {
|
||||||
|
struct _FrameDecider_mac_entry *entry = UPPER_OBJECT(node, struct _FrameDecider_mac_entry, list_node);
|
||||||
|
|
||||||
|
// remove from tree
|
||||||
|
FDMacsTree_Remove(&d->macs_tree, 0, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove from peers list
|
||||||
|
if (d->decide_flood_current == &o->list_node) {
|
||||||
|
d->decide_flood_current = LinkedList1Node_Next(d->decide_flood_current);
|
||||||
|
}
|
||||||
|
LinkedList1_Remove(&d->peers_list, &o->list_node);
|
||||||
|
|
||||||
|
// free group entries
|
||||||
|
BFree(o->group_entries);
|
||||||
|
|
||||||
|
// free MAC entries
|
||||||
|
BFree(o->mac_entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameDeciderPeer_Analyze (FrameDeciderPeer *o, const uint8_t *frame, int frame_len)
|
||||||
|
{
|
||||||
|
ASSERT(frame_len >= 0)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
const uint8_t *pos = frame;
|
||||||
|
int len = frame_len;
|
||||||
|
|
||||||
|
if (len < sizeof(struct ethernet_header)) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
struct ethernet_header eh;
|
||||||
|
memcpy(&eh, pos, sizeof(eh));
|
||||||
|
pos += sizeof(struct ethernet_header);
|
||||||
|
len -= sizeof(struct ethernet_header);
|
||||||
|
|
||||||
|
// register source MAC address with this peer
|
||||||
|
add_mac_to_peer(o, eh.source);
|
||||||
|
|
||||||
|
switch (ntoh16(eh.type)) {
|
||||||
|
case ETHERTYPE_IPV4: {
|
||||||
|
// check IPv4 header
|
||||||
|
struct ipv4_header ipv4_header;
|
||||||
|
if (!ipv4_check((uint8_t *)pos, len, &ipv4_header, (uint8_t **)&pos, &len)) {
|
||||||
|
PeerLog(o, BLOG_INFO, "analyze: wrong IP packet");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if it's IGMP
|
||||||
|
if (ntoh8(ipv4_header.protocol) != IPV4_PROTOCOL_IGMP) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check IGMP header
|
||||||
|
if (len < sizeof(struct igmp_base)) {
|
||||||
|
PeerLog(o, BLOG_INFO, "analyze: IGMP: short packet");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
struct igmp_base igmp_base;
|
||||||
|
memcpy(&igmp_base, pos, sizeof(igmp_base));
|
||||||
|
pos += sizeof(struct igmp_base);
|
||||||
|
len -= sizeof(struct igmp_base);
|
||||||
|
|
||||||
|
switch (ntoh8(igmp_base.type)) {
|
||||||
|
case IGMP_TYPE_V2_MEMBERSHIP_REPORT: {
|
||||||
|
// check extra
|
||||||
|
if (len < sizeof(struct igmp_v2_extra)) {
|
||||||
|
PeerLog(o, BLOG_INFO, "analyze: IGMP: short v2 report");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
struct igmp_v2_extra report;
|
||||||
|
memcpy(&report, pos, sizeof(report));
|
||||||
|
pos += sizeof(struct igmp_v2_extra);
|
||||||
|
len -= sizeof(struct igmp_v2_extra);
|
||||||
|
|
||||||
|
// add to group
|
||||||
|
add_group_to_peer(o, report.group);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case IGMP_TYPE_V3_MEMBERSHIP_REPORT: {
|
||||||
|
// check extra
|
||||||
|
if (len < sizeof(struct igmp_v3_report_extra)) {
|
||||||
|
PeerLog(o, BLOG_INFO, "analyze: IGMP: short v3 report");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
struct igmp_v3_report_extra report;
|
||||||
|
memcpy(&report, pos, sizeof(report));
|
||||||
|
pos += sizeof(struct igmp_v3_report_extra);
|
||||||
|
len -= sizeof(struct igmp_v3_report_extra);
|
||||||
|
|
||||||
|
// iterate records
|
||||||
|
uint16_t num_records = ntoh16(report.number_of_group_records);
|
||||||
|
for (int i = 0; i < num_records; i++) {
|
||||||
|
// check record
|
||||||
|
if (len < sizeof(struct igmp_v3_report_record)) {
|
||||||
|
PeerLog(o, BLOG_INFO, "analyze: IGMP: short record header");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
struct igmp_v3_report_record record;
|
||||||
|
memcpy(&record, pos, sizeof(record));
|
||||||
|
pos += sizeof(struct igmp_v3_report_record);
|
||||||
|
len -= sizeof(struct igmp_v3_report_record);
|
||||||
|
|
||||||
|
// iterate sources
|
||||||
|
uint16_t num_sources = ntoh16(record.number_of_sources);
|
||||||
|
int j;
|
||||||
|
for (j = 0; j < num_sources; j++) {
|
||||||
|
// check source
|
||||||
|
if (len < sizeof(struct igmp_source)) {
|
||||||
|
PeerLog(o, BLOG_INFO, "analyze: IGMP: short source");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
pos += sizeof(struct igmp_source);
|
||||||
|
len -= sizeof(struct igmp_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check aux data
|
||||||
|
uint16_t aux_len = ntoh16(record.aux_data_len);
|
||||||
|
if (len < aux_len) {
|
||||||
|
PeerLog(o, BLOG_INFO, "analyze: IGMP: short record aux data");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
pos += aux_len;
|
||||||
|
len -= aux_len;
|
||||||
|
|
||||||
|
switch (record.type) {
|
||||||
|
case IGMP_RECORD_TYPE_MODE_IS_INCLUDE:
|
||||||
|
case IGMP_RECORD_TYPE_CHANGE_TO_INCLUDE_MODE:
|
||||||
|
if (num_sources != 0) {
|
||||||
|
add_group_to_peer(o, record.group);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IGMP_RECORD_TYPE_MODE_IS_EXCLUDE:
|
||||||
|
case IGMP_RECORD_TYPE_CHANGE_TO_EXCLUDE_MODE:
|
||||||
|
add_group_to_peer(o, record.group);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:;
|
||||||
|
}
|
|
@ -0,0 +1,196 @@
|
||||||
|
/**
|
||||||
|
* @file FrameDecider.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @section DESCRIPTION
|
||||||
|
*
|
||||||
|
* Mudule which decides to which peers frames from the device are to be
|
||||||
|
* forwarded.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_CLIENT_FRAMEDECIDER_H
|
||||||
|
#define BADVPN_CLIENT_FRAMEDECIDER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <structure/LinkedList1.h>
|
||||||
|
#include <structure/LinkedList3.h>
|
||||||
|
#include <structure/SAvl.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <base/BLog.h>
|
||||||
|
#include <system/BReactor.h>
|
||||||
|
|
||||||
|
struct _FrameDeciderPeer;
|
||||||
|
struct _FrameDecider_mac_entry;
|
||||||
|
struct _FrameDecider_group_entry;
|
||||||
|
|
||||||
|
typedef const uint8_t *FDMacsTree_key;
|
||||||
|
|
||||||
|
#include "FrameDecider_macs_tree.h"
|
||||||
|
#include <structure/SAvl_decl.h>
|
||||||
|
|
||||||
|
#include "FrameDecider_groups_tree.h"
|
||||||
|
#include <structure/SAvl_decl.h>
|
||||||
|
|
||||||
|
#include "FrameDecider_multicast_tree.h"
|
||||||
|
#include <structure/SAvl_decl.h>
|
||||||
|
|
||||||
|
struct _FrameDecider_mac_entry {
|
||||||
|
struct _FrameDeciderPeer *peer;
|
||||||
|
LinkedList1Node list_node; // node in FrameDeciderPeer.mac_entries_free or FrameDeciderPeer.mac_entries_used
|
||||||
|
// defined when used:
|
||||||
|
uint8_t mac[6];
|
||||||
|
FDMacsTreeNode tree_node; // node in FrameDecider.macs_tree, indexed by mac
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _FrameDecider_group_entry {
|
||||||
|
struct _FrameDeciderPeer *peer;
|
||||||
|
LinkedList1Node list_node; // node in FrameDeciderPeer.group_entries_free or FrameDeciderPeer.group_entries_used
|
||||||
|
BTimer timer; // timer for removing the group entry, running when used
|
||||||
|
// defined when used:
|
||||||
|
// basic group data
|
||||||
|
uint32_t group; // group address
|
||||||
|
FDGroupsTreeNode tree_node; // node in FrameDeciderPeer.groups_tree, indexed by group
|
||||||
|
// all that folows is managed by add_to_multicast() and remove_from_multicast()
|
||||||
|
LinkedList3Node sig_list_node; // node in list of group entries with the same sig
|
||||||
|
btime_t timer_endtime;
|
||||||
|
int is_master;
|
||||||
|
// defined when used and we are master:
|
||||||
|
struct {
|
||||||
|
uint32_t sig; // last 23 bits of group address
|
||||||
|
FDMulticastTreeNode tree_node; // node in FrameDecider.multicast_tree, indexed by sig
|
||||||
|
} master;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object that represents a local device.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int max_peer_macs;
|
||||||
|
int max_peer_groups;
|
||||||
|
btime_t igmp_group_membership_interval;
|
||||||
|
btime_t igmp_last_member_query_time;
|
||||||
|
BReactor *reactor;
|
||||||
|
LinkedList1 peers_list;
|
||||||
|
FDMacsTree macs_tree;
|
||||||
|
FDMulticastTree multicast_tree;
|
||||||
|
int decide_state;
|
||||||
|
LinkedList1Node *decide_flood_current;
|
||||||
|
struct _FrameDeciderPeer *decide_unicast_peer;
|
||||||
|
LinkedList3Iterator decide_multicast_it;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} FrameDecider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object that represents a peer that a local device can send frames to.
|
||||||
|
*/
|
||||||
|
typedef struct _FrameDeciderPeer {
|
||||||
|
FrameDecider *d;
|
||||||
|
void *user;
|
||||||
|
BLog_logfunc logfunc;
|
||||||
|
struct _FrameDecider_mac_entry *mac_entries;
|
||||||
|
struct _FrameDecider_group_entry *group_entries;
|
||||||
|
LinkedList1Node list_node; // node in FrameDecider.peers_list
|
||||||
|
LinkedList1 mac_entries_free;
|
||||||
|
LinkedList1 mac_entries_used;
|
||||||
|
LinkedList1 group_entries_free;
|
||||||
|
LinkedList1 group_entries_used;
|
||||||
|
FDGroupsTree groups_tree;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} FrameDeciderPeer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the object.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param max_peer_macs maximum number of MAC addresses a peer may posess. Must be >0.
|
||||||
|
* @param max_peer_groups maximum number of multicast groups a peer may belong to. Must be >0.
|
||||||
|
* @param igmp_group_membership_interval IGMP Group Membership Interval value. When a join
|
||||||
|
* is detected for a peer in {@link FrameDeciderPeer_Analyze}, this is how long we wait
|
||||||
|
* for another join before we remove the group from the peer. Note that the group may
|
||||||
|
* be removed sooner if the peer fails to respond to a Group-Specific Query (see below).
|
||||||
|
* @param igmp_last_member_query_time IGMP Last Member Query Time value. When a Group-Specific
|
||||||
|
* Query is detected in {@link FrameDecider_AnalyzeAndDecide}, this is how long we wait for a peer
|
||||||
|
* belonging to the group to send a join before we remove the group from it.
|
||||||
|
*/
|
||||||
|
void FrameDecider_Init (FrameDecider *o, int max_peer_macs, int max_peer_groups, btime_t igmp_group_membership_interval, btime_t igmp_last_member_query_time, BReactor *reactor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the object.
|
||||||
|
* There must be no {@link FrameDeciderPeer} objects using this decider.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void FrameDecider_Free (FrameDecider *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Analyzes a frame read from the local device and starts deciding which peers
|
||||||
|
* the frame should be forwarded to.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param frame frame data
|
||||||
|
* @param frame_len frame length. Must be >=0.
|
||||||
|
*/
|
||||||
|
void FrameDecider_AnalyzeAndDecide (FrameDecider *o, const uint8_t *frame, int frame_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the next peer that the frame submitted to {@link FrameDecider_AnalyzeAndDecide} should be
|
||||||
|
* forwarded to.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @return peer to forward the frame to, or NULL if no more
|
||||||
|
*/
|
||||||
|
FrameDeciderPeer * FrameDecider_NextDestination (FrameDecider *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the object.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param d decider this peer will belong to
|
||||||
|
* @param user argument to log function
|
||||||
|
* @param logfunc function which prepends the log prefix using {@link BLog_Append}
|
||||||
|
* @return 1 on success, 0 on failure
|
||||||
|
*/
|
||||||
|
int FrameDeciderPeer_Init (FrameDeciderPeer *o, FrameDecider *d, void *user, BLog_logfunc logfunc) WARN_UNUSED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the object.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void FrameDeciderPeer_Free (FrameDeciderPeer *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Analyzes a frame received from the peer.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param frame frame data
|
||||||
|
* @param frame_len frame length. Must be >=0.
|
||||||
|
*/
|
||||||
|
void FrameDeciderPeer_Analyze (FrameDeciderPeer *o, const uint8_t *frame, int frame_len);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,9 @@
|
||||||
|
#define SAVL_PARAM_NAME FDGroupsTree
|
||||||
|
#define SAVL_PARAM_FEATURE_COUNTS 0
|
||||||
|
#define SAVL_PARAM_FEATURE_NOKEYS 0
|
||||||
|
#define SAVL_PARAM_TYPE_ENTRY struct _FrameDecider_group_entry
|
||||||
|
#define SAVL_PARAM_TYPE_KEY uint32_t
|
||||||
|
#define SAVL_PARAM_TYPE_ARG int
|
||||||
|
#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) B_COMPARE((entry1)->group, (entry2)->group)
|
||||||
|
#define SAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) B_COMPARE((key1), (entry2)->group)
|
||||||
|
#define SAVL_PARAM_MEMBER_NODE tree_node
|
|
@ -0,0 +1,9 @@
|
||||||
|
#define SAVL_PARAM_NAME FDMacsTree
|
||||||
|
#define SAVL_PARAM_FEATURE_COUNTS 0
|
||||||
|
#define SAVL_PARAM_FEATURE_NOKEYS 0
|
||||||
|
#define SAVL_PARAM_TYPE_ENTRY struct _FrameDecider_mac_entry
|
||||||
|
#define SAVL_PARAM_TYPE_KEY FDMacsTree_key
|
||||||
|
#define SAVL_PARAM_TYPE_ARG int
|
||||||
|
#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) compare_macs((entry1)->mac, (entry2)->mac)
|
||||||
|
#define SAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) compare_macs((key1), (entry2)->mac)
|
||||||
|
#define SAVL_PARAM_MEMBER_NODE tree_node
|
|
@ -0,0 +1,9 @@
|
||||||
|
#define SAVL_PARAM_NAME FDMulticastTree
|
||||||
|
#define SAVL_PARAM_FEATURE_COUNTS 0
|
||||||
|
#define SAVL_PARAM_FEATURE_NOKEYS 0
|
||||||
|
#define SAVL_PARAM_TYPE_ENTRY struct _FrameDecider_group_entry
|
||||||
|
#define SAVL_PARAM_TYPE_KEY uint32_t
|
||||||
|
#define SAVL_PARAM_TYPE_ARG int
|
||||||
|
#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) B_COMPARE((entry1)->master.sig, (entry2)->master.sig)
|
||||||
|
#define SAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) B_COMPARE((key1), (entry2)->master.sig)
|
||||||
|
#define SAVL_PARAM_MEMBER_NODE master.tree_node
|
|
@ -0,0 +1,374 @@
|
||||||
|
/**
|
||||||
|
* @file PasswordListener.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <prerror.h>
|
||||||
|
|
||||||
|
#include <ssl.h>
|
||||||
|
|
||||||
|
#include <misc/offset.h>
|
||||||
|
#include <misc/byteorder.h>
|
||||||
|
#include <misc/balloc.h>
|
||||||
|
#include <misc/compare.h>
|
||||||
|
#include <base/BLog.h>
|
||||||
|
#include <security/BRandom.h>
|
||||||
|
#include <nspr_support/DummyPRFileDesc.h>
|
||||||
|
|
||||||
|
#include <client/PasswordListener.h>
|
||||||
|
|
||||||
|
#include <generated/blog_channel_PasswordListener.h>
|
||||||
|
|
||||||
|
static int password_comparator (void *user, uint64_t *p1, uint64_t *p2);
|
||||||
|
static void remove_client (struct PasswordListenerClient *client);
|
||||||
|
static void listener_handler (PasswordListener *l);
|
||||||
|
static void client_connection_handler (struct PasswordListenerClient *client, int event);
|
||||||
|
static void client_sslcon_handler (struct PasswordListenerClient *client, int event);
|
||||||
|
static void client_receiver_handler (struct PasswordListenerClient *client);
|
||||||
|
|
||||||
|
int password_comparator (void *user, uint64_t *p1, uint64_t *p2)
|
||||||
|
{
|
||||||
|
return B_COMPARE(*p1, *p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_client (struct PasswordListenerClient *client)
|
||||||
|
{
|
||||||
|
PasswordListener *l = client->l;
|
||||||
|
|
||||||
|
// stop using any buffers before they get freed
|
||||||
|
if (l->ssl) {
|
||||||
|
BSSLConnection_ReleaseBuffers(&client->sslcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
// free receiver
|
||||||
|
SingleStreamReceiver_Free(&client->receiver);
|
||||||
|
|
||||||
|
// free SSL
|
||||||
|
if (l->ssl) {
|
||||||
|
BSSLConnection_Free(&client->sslcon);
|
||||||
|
ASSERT_FORCE(PR_Close(client->sock->ssl_prfd) == PR_SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
// free connection interfaces
|
||||||
|
BConnection_RecvAsync_Free(&client->sock->con);
|
||||||
|
BConnection_SendAsync_Free(&client->sock->con);
|
||||||
|
|
||||||
|
// free connection
|
||||||
|
BConnection_Free(&client->sock->con);
|
||||||
|
|
||||||
|
// free sslsocket structure
|
||||||
|
free(client->sock);
|
||||||
|
|
||||||
|
// move to free list
|
||||||
|
LinkedList1_Remove(&l->clients_used, &client->list_node);
|
||||||
|
LinkedList1_Append(&l->clients_free, &client->list_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void listener_handler (PasswordListener *l)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&l->d_obj);
|
||||||
|
|
||||||
|
// obtain client entry
|
||||||
|
if (LinkedList1_IsEmpty(&l->clients_free)) {
|
||||||
|
struct PasswordListenerClient *client = UPPER_OBJECT(LinkedList1_GetFirst(&l->clients_used), struct PasswordListenerClient, list_node);
|
||||||
|
remove_client(client);
|
||||||
|
}
|
||||||
|
struct PasswordListenerClient *client = UPPER_OBJECT(LinkedList1_GetLast(&l->clients_free), struct PasswordListenerClient, list_node);
|
||||||
|
LinkedList1_Remove(&l->clients_free, &client->list_node);
|
||||||
|
LinkedList1_Append(&l->clients_used, &client->list_node);
|
||||||
|
|
||||||
|
// allocate sslsocket structure
|
||||||
|
if (!(client->sock = (sslsocket *)malloc(sizeof(*client->sock)))) {
|
||||||
|
BLog(BLOG_ERROR, "malloc failedt");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// accept connection
|
||||||
|
if (!BConnection_Init(&client->sock->con, BConnection_source_listener(&l->listener, NULL), l->bsys, client, (BConnection_handler)client_connection_handler)) {
|
||||||
|
BLog(BLOG_ERROR, "BConnection_Init failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
BLog(BLOG_INFO, "Connection accepted");
|
||||||
|
|
||||||
|
// init connection interfaces
|
||||||
|
BConnection_SendAsync_Init(&client->sock->con);
|
||||||
|
BConnection_RecvAsync_Init(&client->sock->con);
|
||||||
|
|
||||||
|
StreamPassInterface *send_if = BConnection_SendAsync_GetIf(&client->sock->con);
|
||||||
|
StreamRecvInterface *recv_if = BConnection_RecvAsync_GetIf(&client->sock->con);
|
||||||
|
|
||||||
|
if (l->ssl) {
|
||||||
|
// create bottom NSPR file descriptor
|
||||||
|
if (!BSSLConnection_MakeBackend(&client->sock->bottom_prfd, send_if, recv_if, l->twd, l->ssl_flags)) {
|
||||||
|
BLog(BLOG_ERROR, "BSSLConnection_MakeBackend failed");
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create SSL file descriptor from the bottom NSPR file descriptor
|
||||||
|
if (!(client->sock->ssl_prfd = SSL_ImportFD(l->model_prfd, &client->sock->bottom_prfd))) {
|
||||||
|
ASSERT_FORCE(PR_Close(&client->sock->bottom_prfd) == PR_SUCCESS)
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set server mode
|
||||||
|
if (SSL_ResetHandshake(client->sock->ssl_prfd, PR_TRUE) != SECSuccess) {
|
||||||
|
BLog(BLOG_ERROR, "SSL_ResetHandshake failed");
|
||||||
|
goto fail3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set require client certificate
|
||||||
|
if (SSL_OptionSet(client->sock->ssl_prfd, SSL_REQUEST_CERTIFICATE, PR_TRUE) != SECSuccess) {
|
||||||
|
BLog(BLOG_ERROR, "SSL_OptionSet(SSL_REQUEST_CERTIFICATE) failed");
|
||||||
|
goto fail3;
|
||||||
|
}
|
||||||
|
if (SSL_OptionSet(client->sock->ssl_prfd, SSL_REQUIRE_CERTIFICATE, PR_TRUE) != SECSuccess) {
|
||||||
|
BLog(BLOG_ERROR, "SSL_OptionSet(SSL_REQUIRE_CERTIFICATE) failed");
|
||||||
|
goto fail3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize SSLConnection
|
||||||
|
BSSLConnection_Init(&client->sslcon, client->sock->ssl_prfd, 0, BReactor_PendingGroup(l->bsys), client, (BSSLConnection_handler)client_sslcon_handler);
|
||||||
|
|
||||||
|
send_if = BSSLConnection_GetSendIf(&client->sslcon);
|
||||||
|
recv_if = BSSLConnection_GetRecvIf(&client->sslcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
// init receiver
|
||||||
|
SingleStreamReceiver_Init(&client->receiver, (uint8_t *)&client->recv_buffer, sizeof(client->recv_buffer), recv_if, BReactor_PendingGroup(l->bsys), client, (SingleStreamReceiver_handler)client_receiver_handler);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
// cleanup on error
|
||||||
|
fail3:
|
||||||
|
if (l->ssl) {
|
||||||
|
ASSERT_FORCE(PR_Close(client->sock->ssl_prfd) == PR_SUCCESS)
|
||||||
|
}
|
||||||
|
fail2:
|
||||||
|
BConnection_RecvAsync_Free(&client->sock->con);
|
||||||
|
BConnection_SendAsync_Free(&client->sock->con);
|
||||||
|
BConnection_Free(&client->sock->con);
|
||||||
|
fail1:
|
||||||
|
free(client->sock);
|
||||||
|
fail0:
|
||||||
|
LinkedList1_Remove(&l->clients_used, &client->list_node);
|
||||||
|
LinkedList1_Append(&l->clients_free, &client->list_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void client_connection_handler (struct PasswordListenerClient *client, int event)
|
||||||
|
{
|
||||||
|
PasswordListener *l = client->l;
|
||||||
|
DebugObject_Access(&l->d_obj);
|
||||||
|
|
||||||
|
if (event == BCONNECTION_EVENT_RECVCLOSED) {
|
||||||
|
BLog(BLOG_INFO, "connection closed");
|
||||||
|
} else {
|
||||||
|
BLog(BLOG_INFO, "connection error");
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_client(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
void client_sslcon_handler (struct PasswordListenerClient *client, int event)
|
||||||
|
{
|
||||||
|
PasswordListener *l = client->l;
|
||||||
|
DebugObject_Access(&l->d_obj);
|
||||||
|
ASSERT(l->ssl)
|
||||||
|
ASSERT(event == BSSLCONNECTION_EVENT_ERROR)
|
||||||
|
|
||||||
|
BLog(BLOG_INFO, "SSL error");
|
||||||
|
|
||||||
|
remove_client(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
void client_receiver_handler (struct PasswordListenerClient *client)
|
||||||
|
{
|
||||||
|
PasswordListener *l = client->l;
|
||||||
|
DebugObject_Access(&l->d_obj);
|
||||||
|
|
||||||
|
// check password
|
||||||
|
uint64_t received_pass = ltoh64(client->recv_buffer);
|
||||||
|
BAVLNode *pw_tree_node = BAVL_LookupExact(&l->passwords, &received_pass);
|
||||||
|
if (!pw_tree_node) {
|
||||||
|
BLog(BLOG_WARNING, "unknown password");
|
||||||
|
remove_client(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PasswordListener_pwentry *pw_entry = UPPER_OBJECT(pw_tree_node, PasswordListener_pwentry, tree_node);
|
||||||
|
|
||||||
|
BLog(BLOG_INFO, "Password recognized");
|
||||||
|
|
||||||
|
// remove password entry
|
||||||
|
BAVL_Remove(&l->passwords, &pw_entry->tree_node);
|
||||||
|
|
||||||
|
// stop using any buffers before they get freed
|
||||||
|
if (l->ssl) {
|
||||||
|
BSSLConnection_ReleaseBuffers(&client->sslcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
// free receiver
|
||||||
|
SingleStreamReceiver_Free(&client->receiver);
|
||||||
|
|
||||||
|
if (l->ssl) {
|
||||||
|
// free SSL connection
|
||||||
|
BSSLConnection_Free(&client->sslcon);
|
||||||
|
} else {
|
||||||
|
// free connection interfaces
|
||||||
|
BConnection_RecvAsync_Free(&client->sock->con);
|
||||||
|
BConnection_SendAsync_Free(&client->sock->con);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove connection handler
|
||||||
|
BConnection_SetHandlers(&client->sock->con, NULL, NULL);
|
||||||
|
|
||||||
|
// move client entry to free list
|
||||||
|
LinkedList1_Remove(&l->clients_used, &client->list_node);
|
||||||
|
LinkedList1_Append(&l->clients_free, &client->list_node);
|
||||||
|
|
||||||
|
// give the socket to the handler
|
||||||
|
pw_entry->handler_client(pw_entry->user, client->sock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PasswordListener_Init (PasswordListener *l, BReactor *bsys, BThreadWorkDispatcher *twd, BAddr listen_addr, int max_clients, int ssl, int ssl_flags, CERTCertificate *cert, SECKEYPrivateKey *key)
|
||||||
|
{
|
||||||
|
ASSERT(BConnection_AddressSupported(listen_addr))
|
||||||
|
ASSERT(max_clients > 0)
|
||||||
|
ASSERT(ssl == 0 || ssl == 1)
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
l->bsys = bsys;
|
||||||
|
l->twd = twd;
|
||||||
|
l->ssl = ssl;
|
||||||
|
l->ssl_flags = ssl_flags;
|
||||||
|
|
||||||
|
// allocate client entries
|
||||||
|
if (!(l->clients_data = (struct PasswordListenerClient *)BAllocArray(max_clients, sizeof(struct PasswordListenerClient)))) {
|
||||||
|
BLog(BLOG_ERROR, "BAllocArray failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (l->ssl) {
|
||||||
|
// initialize model SSL fd
|
||||||
|
DummyPRFileDesc_Create(&l->model_dprfd);
|
||||||
|
if (!(l->model_prfd = SSL_ImportFD(NULL, &l->model_dprfd))) {
|
||||||
|
BLog(BLOG_ERROR, "SSL_ImportFD failed");
|
||||||
|
ASSERT_FORCE(PR_Close(&l->model_dprfd) == PR_SUCCESS)
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set server certificate
|
||||||
|
if (SSL_ConfigSecureServer(l->model_prfd, cert, key, NSS_FindCertKEAType(cert)) != SECSuccess) {
|
||||||
|
BLog(BLOG_ERROR, "SSL_ConfigSecureServer failed");
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize client entries
|
||||||
|
LinkedList1_Init(&l->clients_free);
|
||||||
|
LinkedList1_Init(&l->clients_used);
|
||||||
|
for (int i = 0; i < max_clients; i++) {
|
||||||
|
struct PasswordListenerClient *conn = &l->clients_data[i];
|
||||||
|
conn->l = l;
|
||||||
|
LinkedList1_Append(&l->clients_free, &conn->list_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize passwords tree
|
||||||
|
BAVL_Init(&l->passwords, OFFSET_DIFF(PasswordListener_pwentry, password, tree_node), (BAVL_comparator)password_comparator, NULL);
|
||||||
|
|
||||||
|
// initialize listener
|
||||||
|
if (!BListener_Init(&l->listener, listen_addr, l->bsys, l, (BListener_handler)listener_handler)) {
|
||||||
|
BLog(BLOG_ERROR, "Listener_Init failed");
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugObject_Init(&l->d_obj);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
fail2:
|
||||||
|
if (l->ssl) {
|
||||||
|
ASSERT_FORCE(PR_Close(l->model_prfd) == PR_SUCCESS)
|
||||||
|
}
|
||||||
|
fail1:
|
||||||
|
BFree(l->clients_data);
|
||||||
|
fail0:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PasswordListener_Free (PasswordListener *l)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&l->d_obj);
|
||||||
|
|
||||||
|
// free clients
|
||||||
|
LinkedList1Node *node;
|
||||||
|
while (node = LinkedList1_GetFirst(&l->clients_used)) {
|
||||||
|
struct PasswordListenerClient *client = UPPER_OBJECT(node, struct PasswordListenerClient, list_node);
|
||||||
|
remove_client(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
// free listener
|
||||||
|
BListener_Free(&l->listener);
|
||||||
|
|
||||||
|
// free model SSL file descriptor
|
||||||
|
if (l->ssl) {
|
||||||
|
ASSERT_FORCE(PR_Close(l->model_prfd) == PR_SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
// free client entries
|
||||||
|
BFree(l->clients_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t PasswordListener_AddEntry (PasswordListener *l, PasswordListener_pwentry *entry, PasswordListener_handler_client handler_client, void *user)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&l->d_obj);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
// generate password
|
||||||
|
BRandom_randomize((uint8_t *)&entry->password, sizeof(entry->password));
|
||||||
|
|
||||||
|
// try inserting
|
||||||
|
if (BAVL_Insert(&l->passwords, &entry->tree_node, NULL)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->handler_client = handler_client;
|
||||||
|
entry->user = user;
|
||||||
|
|
||||||
|
return entry->password;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PasswordListener_RemoveEntry (PasswordListener *l, PasswordListener_pwentry *entry)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&l->d_obj);
|
||||||
|
|
||||||
|
// remove
|
||||||
|
BAVL_Remove(&l->passwords, &entry->tree_node);
|
||||||
|
}
|
|
@ -0,0 +1,156 @@
|
||||||
|
/**
|
||||||
|
* @file PasswordListener.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @section DESCRIPTION
|
||||||
|
*
|
||||||
|
* Object used to listen on a socket, accept clients and identify them
|
||||||
|
* based on a number they send.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_CLIENT_PASSWORDLISTENER_H
|
||||||
|
#define BADVPN_CLIENT_PASSWORDLISTENER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <prio.h>
|
||||||
|
|
||||||
|
#include <cert.h>
|
||||||
|
#include <keyhi.h>
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <misc/sslsocket.h>
|
||||||
|
#include <structure/LinkedList1.h>
|
||||||
|
#include <structure/BAVL.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <flow/SingleStreamReceiver.h>
|
||||||
|
#include <system/BConnection.h>
|
||||||
|
#include <nspr_support/BSSLConnection.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler function called when a client identifies itself with a password
|
||||||
|
* belonging to one of the password entries.
|
||||||
|
* The password entry is unregistered before the handler is called
|
||||||
|
* and must not be unregistered again.
|
||||||
|
*
|
||||||
|
* @param user as in {@link PasswordListener_AddEntry}
|
||||||
|
* @param sock structure containing a {@link BConnection} and, if TLS is enabled,
|
||||||
|
* the SSL socket with the bottom layer connected to the async interfaces
|
||||||
|
* of the {@link BConnection} object. The structure was allocated with
|
||||||
|
* malloc() and the user is responsible for freeing it.
|
||||||
|
*/
|
||||||
|
typedef void (*PasswordListener_handler_client) (void *user, sslsocket *sock);
|
||||||
|
|
||||||
|
struct PasswordListenerClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object used to listen on a socket, accept clients and identify them
|
||||||
|
* based on a number they send.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
BReactor *bsys;
|
||||||
|
BThreadWorkDispatcher *twd;
|
||||||
|
int ssl;
|
||||||
|
int ssl_flags;
|
||||||
|
PRFileDesc model_dprfd;
|
||||||
|
PRFileDesc *model_prfd;
|
||||||
|
struct PasswordListenerClient *clients_data;
|
||||||
|
LinkedList1 clients_free;
|
||||||
|
LinkedList1 clients_used;
|
||||||
|
BAVL passwords;
|
||||||
|
BListener listener;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} PasswordListener;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t password;
|
||||||
|
BAVLNode tree_node;
|
||||||
|
PasswordListener_handler_client handler_client;
|
||||||
|
void *user;
|
||||||
|
} PasswordListener_pwentry;
|
||||||
|
|
||||||
|
struct PasswordListenerClient {
|
||||||
|
PasswordListener *l;
|
||||||
|
LinkedList1Node list_node;
|
||||||
|
sslsocket *sock;
|
||||||
|
BSSLConnection sslcon;
|
||||||
|
SingleStreamReceiver receiver;
|
||||||
|
uint64_t recv_buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the object.
|
||||||
|
*
|
||||||
|
* @param l the object
|
||||||
|
* @param bsys reactor we live in
|
||||||
|
* @param twd thread work dispatcher. May be NULL if ssl_flags does not request performing SSL
|
||||||
|
* operations in threads.
|
||||||
|
* @param listen_addr address to listen on. Must be supported according to {@link BConnection_AddressSupported}.
|
||||||
|
* @param max_clients maximum number of client to hold until they are identified.
|
||||||
|
* Must be >0.
|
||||||
|
* @param ssl whether to use TLS. Must be 1 or 0.
|
||||||
|
* @param ssl_flags flags passed down to {@link BSSLConnection_MakeBackend}. May be used to
|
||||||
|
* request performing SSL operations in threads.
|
||||||
|
* @param cert if using TLS, the server certificate
|
||||||
|
* @param key if using TLS, the private key
|
||||||
|
* @return 1 on success, 0 on failure
|
||||||
|
*/
|
||||||
|
int PasswordListener_Init (PasswordListener *l, BReactor *bsys, BThreadWorkDispatcher *twd, BAddr listen_addr, int max_clients, int ssl, int ssl_flags, CERTCertificate *cert, SECKEYPrivateKey *key) WARN_UNUSED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the object.
|
||||||
|
*
|
||||||
|
* @param l the object
|
||||||
|
*/
|
||||||
|
void PasswordListener_Free (PasswordListener *l);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a password entry.
|
||||||
|
*
|
||||||
|
* @param l the object
|
||||||
|
* @param entry uninitialized entry structure
|
||||||
|
* @param handler_client handler function to call when a client identifies
|
||||||
|
* with the password which this function returns
|
||||||
|
* @param user value to pass to handler function
|
||||||
|
* @return password which a client should send to be recognized and
|
||||||
|
* dispatched to the handler function. Should be treated as a numeric
|
||||||
|
* value, which a client should as a little-endian 64-bit unsigned integer
|
||||||
|
* when it connects.
|
||||||
|
*/
|
||||||
|
uint64_t PasswordListener_AddEntry (PasswordListener *l, PasswordListener_pwentry *entry, PasswordListener_handler_client handler_client, void *user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters a password entry.
|
||||||
|
* Note that when a client is dispatched, its entry is unregistered
|
||||||
|
* automatically and must not be unregistered again here.
|
||||||
|
*
|
||||||
|
* @param l the object
|
||||||
|
* @param entry entry to unregister
|
||||||
|
*/
|
||||||
|
void PasswordListener_RemoveEntry (PasswordListener *l, PasswordListener_pwentry *entry);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,433 @@
|
||||||
|
/**
|
||||||
|
* @file PeerChat.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <ssl.h>
|
||||||
|
#include <sslerr.h>
|
||||||
|
|
||||||
|
#include <misc/byteorder.h>
|
||||||
|
#include <security/BRandom.h>
|
||||||
|
|
||||||
|
#include "PeerChat.h"
|
||||||
|
|
||||||
|
#include <generated/blog_channel_PeerChat.h>
|
||||||
|
|
||||||
|
#define PeerLog(_o, ...) BLog_LogViaFunc((_o)->logfunc, (_o)->user, BLOG_CURRENT_CHANNEL, __VA_ARGS__)
|
||||||
|
|
||||||
|
static void report_error (PeerChat *o)
|
||||||
|
{
|
||||||
|
DebugError_AssertNoError(&o->d_err);
|
||||||
|
|
||||||
|
DEBUGERROR(&o->d_err, o->handler_error(o->user))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void recv_job_handler (PeerChat *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
DebugError_AssertNoError(&o->d_err);
|
||||||
|
ASSERT(o->recv_data_len >= 0)
|
||||||
|
ASSERT(o->recv_data_len <= SC_MAX_MSGLEN)
|
||||||
|
|
||||||
|
int data_len = o->recv_data_len;
|
||||||
|
|
||||||
|
// set no received data
|
||||||
|
o->recv_data_len = -1;
|
||||||
|
|
||||||
|
#ifdef PEERCHAT_SIMULATE_ERROR
|
||||||
|
uint8_t x;
|
||||||
|
BRandom_randomize(&x, sizeof(x));
|
||||||
|
if (x < PEERCHAT_SIMULATE_ERROR) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "simulate error");
|
||||||
|
report_error(o);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (o->ssl_mode != PEERCHAT_SSL_NONE) {
|
||||||
|
// buffer data
|
||||||
|
if (!SimpleStreamBuffer_Write(&o->ssl_recv_buf, o->recv_data, data_len)) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "out of recv buffer");
|
||||||
|
report_error(o);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// call message handler
|
||||||
|
o->handler_message(o->user, o->recv_data, data_len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ssl_con_handler (PeerChat *o, int event)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
DebugError_AssertNoError(&o->d_err);
|
||||||
|
ASSERT(o->ssl_mode == PEERCHAT_SSL_CLIENT || o->ssl_mode == PEERCHAT_SSL_SERVER)
|
||||||
|
ASSERT(event == BSSLCONNECTION_EVENT_ERROR)
|
||||||
|
|
||||||
|
PeerLog(o, BLOG_ERROR, "SSL error");
|
||||||
|
|
||||||
|
report_error(o);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SECStatus client_auth_data_callback (PeerChat *o, PRFileDesc *fd, CERTDistNames *caNames, CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(o->ssl_mode == PEERCHAT_SSL_CLIENT)
|
||||||
|
|
||||||
|
CERTCertificate *cert = CERT_DupCertificate(o->ssl_cert);
|
||||||
|
if (!cert) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "CERT_DupCertificate failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECKEYPrivateKey *key = SECKEY_CopyPrivateKey(o->ssl_key);
|
||||||
|
if (!key) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "SECKEY_CopyPrivateKey failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pRetCert = cert;
|
||||||
|
*pRetKey = key;
|
||||||
|
return SECSuccess;
|
||||||
|
|
||||||
|
fail1:
|
||||||
|
CERT_DestroyCertificate(cert);
|
||||||
|
fail0:
|
||||||
|
return SECFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SECStatus auth_certificate_callback (PeerChat *o, PRFileDesc *fd, PRBool checkSig, PRBool isServer)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(o->ssl_mode == PEERCHAT_SSL_CLIENT || o->ssl_mode == PEERCHAT_SSL_SERVER)
|
||||||
|
|
||||||
|
// This callback is used to bypass checking the server's domain name, as peers
|
||||||
|
// don't have domain names. We byte-compare the certificate to the one reported
|
||||||
|
// by the server anyway.
|
||||||
|
|
||||||
|
SECStatus ret = SECFailure;
|
||||||
|
|
||||||
|
CERTCertificate *cert = SSL_PeerCertificate(o->ssl_prfd);
|
||||||
|
if (!cert) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "SSL_PeerCertificate failed");
|
||||||
|
PORT_SetError(SSL_ERROR_BAD_CERTIFICATE);
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECCertUsage cert_usage = (o->ssl_mode == PEERCHAT_SSL_CLIENT ? certUsageSSLServer : certUsageSSLClient);
|
||||||
|
|
||||||
|
if (CERT_VerifyCertNow(CERT_GetDefaultCertDB(), cert, PR_TRUE, cert_usage, SSL_RevealPinArg(o->ssl_prfd)) != SECSuccess) {
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare to certificate provided by the server
|
||||||
|
SECItem der = cert->derCert;
|
||||||
|
if (der.len != o->ssl_peer_cert_len || memcmp(der.data, o->ssl_peer_cert, der.len)) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "peer certificate doesn't match");
|
||||||
|
PORT_SetError(SSL_ERROR_BAD_CERTIFICATE);
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = SECSuccess;
|
||||||
|
|
||||||
|
fail2:
|
||||||
|
CERT_DestroyCertificate(cert);
|
||||||
|
fail1:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ssl_recv_if_handler_send (PeerChat *o, uint8_t *data, int data_len)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
DebugError_AssertNoError(&o->d_err);
|
||||||
|
ASSERT(o->ssl_mode == PEERCHAT_SSL_CLIENT || o->ssl_mode == PEERCHAT_SSL_SERVER)
|
||||||
|
ASSERT(data_len >= 0)
|
||||||
|
ASSERT(data_len <= SC_MAX_MSGLEN)
|
||||||
|
|
||||||
|
// accept packet
|
||||||
|
PacketPassInterface_Done(&o->ssl_recv_if);
|
||||||
|
|
||||||
|
// call message handler
|
||||||
|
o->handler_message(o->user, data, data_len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ssl_recv_decoder_handler_error (PeerChat *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
DebugError_AssertNoError(&o->d_err);
|
||||||
|
ASSERT(o->ssl_mode == PEERCHAT_SSL_CLIENT || o->ssl_mode == PEERCHAT_SSL_SERVER)
|
||||||
|
|
||||||
|
PeerLog(o, BLOG_ERROR, "decoder error");
|
||||||
|
|
||||||
|
report_error(o);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PeerChat_Init (PeerChat *o, peerid_t peer_id, int ssl_mode, int ssl_flags, CERTCertificate *ssl_cert, SECKEYPrivateKey *ssl_key,
|
||||||
|
uint8_t *ssl_peer_cert, int ssl_peer_cert_len, BPendingGroup *pg, BThreadWorkDispatcher *twd, void *user,
|
||||||
|
BLog_logfunc logfunc,
|
||||||
|
PeerChat_handler_error handler_error,
|
||||||
|
PeerChat_handler_message handler_message)
|
||||||
|
{
|
||||||
|
ASSERT(ssl_mode == PEERCHAT_SSL_NONE || ssl_mode == PEERCHAT_SSL_CLIENT || ssl_mode == PEERCHAT_SSL_SERVER)
|
||||||
|
ASSERT(ssl_mode == PEERCHAT_SSL_NONE || ssl_peer_cert_len >= 0)
|
||||||
|
ASSERT(logfunc)
|
||||||
|
ASSERT(handler_error)
|
||||||
|
ASSERT(handler_message)
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
o->ssl_mode = ssl_mode;
|
||||||
|
o->ssl_cert = ssl_cert;
|
||||||
|
o->ssl_key = ssl_key;
|
||||||
|
o->ssl_peer_cert = ssl_peer_cert;
|
||||||
|
o->ssl_peer_cert_len = ssl_peer_cert_len;
|
||||||
|
o->user = user;
|
||||||
|
o->logfunc = logfunc;
|
||||||
|
o->handler_error = handler_error;
|
||||||
|
o->handler_message = handler_message;
|
||||||
|
|
||||||
|
// init copier
|
||||||
|
PacketCopier_Init(&o->copier, SC_MAX_MSGLEN, pg);
|
||||||
|
|
||||||
|
// init SC encoder
|
||||||
|
SCOutmsgEncoder_Init(&o->sc_encoder, peer_id, PacketCopier_GetOutput(&o->copier), pg);
|
||||||
|
|
||||||
|
// init PacketProto encoder
|
||||||
|
PacketProtoEncoder_Init(&o->pp_encoder, SCOutmsgEncoder_GetOutput(&o->sc_encoder), pg);
|
||||||
|
|
||||||
|
// init recv job
|
||||||
|
BPending_Init(&o->recv_job, pg, (BPending_handler)recv_job_handler, o);
|
||||||
|
|
||||||
|
// set no received data
|
||||||
|
o->recv_data_len = -1;
|
||||||
|
|
||||||
|
PacketPassInterface *send_buf_output = PacketCopier_GetInput(&o->copier);
|
||||||
|
|
||||||
|
if (o->ssl_mode != PEERCHAT_SSL_NONE) {
|
||||||
|
// init receive buffer
|
||||||
|
if (!SimpleStreamBuffer_Init(&o->ssl_recv_buf, PEERCHAT_SSL_RECV_BUF_SIZE, pg)) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "SimpleStreamBuffer_Init failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init SSL StreamPacketSender
|
||||||
|
StreamPacketSender_Init(&o->ssl_sp_sender, send_buf_output, pg);
|
||||||
|
|
||||||
|
// init SSL bottom prfd
|
||||||
|
if (!BSSLConnection_MakeBackend(&o->ssl_bottom_prfd, StreamPacketSender_GetInput(&o->ssl_sp_sender), SimpleStreamBuffer_GetOutput(&o->ssl_recv_buf), twd, ssl_flags)) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "BSSLConnection_MakeBackend failed");
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init SSL prfd
|
||||||
|
if (!(o->ssl_prfd = SSL_ImportFD(NULL, &o->ssl_bottom_prfd))) {
|
||||||
|
ASSERT_FORCE(PR_Close(&o->ssl_bottom_prfd) == PR_SUCCESS)
|
||||||
|
PeerLog(o, BLOG_ERROR, "SSL_ImportFD failed");
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set client or server mode
|
||||||
|
if (SSL_ResetHandshake(o->ssl_prfd, (o->ssl_mode == PEERCHAT_SSL_SERVER ? PR_TRUE : PR_FALSE)) != SECSuccess) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "SSL_ResetHandshake failed");
|
||||||
|
goto fail3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o->ssl_mode == PEERCHAT_SSL_SERVER) {
|
||||||
|
// set server certificate
|
||||||
|
if (SSL_ConfigSecureServer(o->ssl_prfd, o->ssl_cert, o->ssl_key, NSS_FindCertKEAType(o->ssl_cert)) != SECSuccess) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "SSL_ConfigSecureServer failed");
|
||||||
|
goto fail3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set require client certificate
|
||||||
|
if (SSL_OptionSet(o->ssl_prfd, SSL_REQUEST_CERTIFICATE, PR_TRUE) != SECSuccess) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "SSL_OptionSet(SSL_REQUEST_CERTIFICATE) failed");
|
||||||
|
goto fail3;
|
||||||
|
}
|
||||||
|
if (SSL_OptionSet(o->ssl_prfd, SSL_REQUIRE_CERTIFICATE, PR_TRUE) != SECSuccess) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "SSL_OptionSet(SSL_REQUIRE_CERTIFICATE) failed");
|
||||||
|
goto fail3;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// set client certificate callback
|
||||||
|
if (SSL_GetClientAuthDataHook(o->ssl_prfd, (SSLGetClientAuthData)client_auth_data_callback, o) != SECSuccess) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "SSL_GetClientAuthDataHook failed");
|
||||||
|
goto fail3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set verify peer certificate hook
|
||||||
|
if (SSL_AuthCertificateHook(o->ssl_prfd, (SSLAuthCertificate)auth_certificate_callback, o) != SECSuccess) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "SSL_AuthCertificateHook failed");
|
||||||
|
goto fail3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init SSL connection
|
||||||
|
BSSLConnection_Init(&o->ssl_con, o->ssl_prfd, 0, pg, o, (BSSLConnection_handler)ssl_con_handler);
|
||||||
|
|
||||||
|
// init SSL PacketStreamSender
|
||||||
|
PacketStreamSender_Init(&o->ssl_ps_sender, BSSLConnection_GetSendIf(&o->ssl_con), sizeof(struct packetproto_header) + SC_MAX_MSGLEN, pg);
|
||||||
|
|
||||||
|
// init SSL copier
|
||||||
|
PacketCopier_Init(&o->ssl_copier, SC_MAX_MSGLEN, pg);
|
||||||
|
|
||||||
|
// init SSL encoder
|
||||||
|
PacketProtoEncoder_Init(&o->ssl_encoder, PacketCopier_GetOutput(&o->ssl_copier), pg);
|
||||||
|
|
||||||
|
// init SSL buffer
|
||||||
|
if (!SinglePacketBuffer_Init(&o->ssl_buffer, PacketProtoEncoder_GetOutput(&o->ssl_encoder), PacketStreamSender_GetInput(&o->ssl_ps_sender), pg)) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "SinglePacketBuffer_Init failed");
|
||||||
|
goto fail4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init receive interface
|
||||||
|
PacketPassInterface_Init(&o->ssl_recv_if, SC_MAX_MSGLEN, (PacketPassInterface_handler_send)ssl_recv_if_handler_send, o, pg);
|
||||||
|
|
||||||
|
// init receive decoder
|
||||||
|
if (!PacketProtoDecoder_Init(&o->ssl_recv_decoder, BSSLConnection_GetRecvIf(&o->ssl_con), &o->ssl_recv_if, pg, o, (PacketProtoDecoder_handler_error)ssl_recv_decoder_handler_error)) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "PacketProtoDecoder_Init failed");
|
||||||
|
goto fail5;
|
||||||
|
}
|
||||||
|
|
||||||
|
send_buf_output = PacketCopier_GetInput(&o->ssl_copier);
|
||||||
|
}
|
||||||
|
|
||||||
|
// init send writer
|
||||||
|
BufferWriter_Init(&o->send_writer, SC_MAX_MSGLEN, pg);
|
||||||
|
|
||||||
|
// init send buffer
|
||||||
|
if (!PacketBuffer_Init(&o->send_buf, BufferWriter_GetOutput(&o->send_writer), send_buf_output, PEERCHAT_SEND_BUF_SIZE, pg)) {
|
||||||
|
PeerLog(o, BLOG_ERROR, "PacketBuffer_Init failed");
|
||||||
|
goto fail6;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugError_Init(&o->d_err, pg);
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail6:
|
||||||
|
BufferWriter_Free(&o->send_writer);
|
||||||
|
if (o->ssl_mode != PEERCHAT_SSL_NONE) {
|
||||||
|
PacketProtoDecoder_Free(&o->ssl_recv_decoder);
|
||||||
|
fail5:
|
||||||
|
PacketPassInterface_Free(&o->ssl_recv_if);
|
||||||
|
SinglePacketBuffer_Free(&o->ssl_buffer);
|
||||||
|
fail4:
|
||||||
|
PacketProtoEncoder_Free(&o->ssl_encoder);
|
||||||
|
PacketCopier_Free(&o->ssl_copier);
|
||||||
|
PacketStreamSender_Free(&o->ssl_ps_sender);
|
||||||
|
BSSLConnection_Free(&o->ssl_con);
|
||||||
|
fail3:
|
||||||
|
ASSERT_FORCE(PR_Close(o->ssl_prfd) == PR_SUCCESS)
|
||||||
|
fail2:
|
||||||
|
StreamPacketSender_Free(&o->ssl_sp_sender);
|
||||||
|
SimpleStreamBuffer_Free(&o->ssl_recv_buf);
|
||||||
|
}
|
||||||
|
fail1:
|
||||||
|
BPending_Free(&o->recv_job);
|
||||||
|
PacketProtoEncoder_Free(&o->pp_encoder);
|
||||||
|
SCOutmsgEncoder_Free(&o->sc_encoder);
|
||||||
|
PacketCopier_Free(&o->copier);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PeerChat_Free (PeerChat *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
DebugError_Free(&o->d_err);
|
||||||
|
|
||||||
|
// stop using any buffers before they get freed
|
||||||
|
if (o->ssl_mode != PEERCHAT_SSL_NONE) {
|
||||||
|
BSSLConnection_ReleaseBuffers(&o->ssl_con);
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketBuffer_Free(&o->send_buf);
|
||||||
|
BufferWriter_Free(&o->send_writer);
|
||||||
|
if (o->ssl_mode != PEERCHAT_SSL_NONE) {
|
||||||
|
PacketProtoDecoder_Free(&o->ssl_recv_decoder);
|
||||||
|
PacketPassInterface_Free(&o->ssl_recv_if);
|
||||||
|
SinglePacketBuffer_Free(&o->ssl_buffer);
|
||||||
|
PacketProtoEncoder_Free(&o->ssl_encoder);
|
||||||
|
PacketCopier_Free(&o->ssl_copier);
|
||||||
|
PacketStreamSender_Free(&o->ssl_ps_sender);
|
||||||
|
BSSLConnection_Free(&o->ssl_con);
|
||||||
|
ASSERT_FORCE(PR_Close(o->ssl_prfd) == PR_SUCCESS)
|
||||||
|
StreamPacketSender_Free(&o->ssl_sp_sender);
|
||||||
|
SimpleStreamBuffer_Free(&o->ssl_recv_buf);
|
||||||
|
}
|
||||||
|
BPending_Free(&o->recv_job);
|
||||||
|
PacketProtoEncoder_Free(&o->pp_encoder);
|
||||||
|
SCOutmsgEncoder_Free(&o->sc_encoder);
|
||||||
|
PacketCopier_Free(&o->copier);
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketRecvInterface * PeerChat_GetSendOutput (PeerChat *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
return PacketProtoEncoder_GetOutput(&o->pp_encoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PeerChat_InputReceived (PeerChat *o, uint8_t *data, int data_len)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
DebugError_AssertNoError(&o->d_err);
|
||||||
|
ASSERT(o->recv_data_len == -1)
|
||||||
|
ASSERT(data_len >= 0)
|
||||||
|
ASSERT(data_len <= SC_MAX_MSGLEN)
|
||||||
|
|
||||||
|
// remember data
|
||||||
|
o->recv_data = data;
|
||||||
|
o->recv_data_len = data_len;
|
||||||
|
|
||||||
|
// set received job
|
||||||
|
BPending_Set(&o->recv_job);
|
||||||
|
}
|
||||||
|
|
||||||
|
int PeerChat_StartMessage (PeerChat *o, uint8_t **data)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
DebugError_AssertNoError(&o->d_err);
|
||||||
|
|
||||||
|
return BufferWriter_StartPacket(&o->send_writer, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PeerChat_EndMessage (PeerChat *o, int data_len)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
DebugError_AssertNoError(&o->d_err);
|
||||||
|
ASSERT(data_len >= 0)
|
||||||
|
ASSERT(data_len <= SC_MAX_MSGLEN)
|
||||||
|
|
||||||
|
BufferWriter_EndPacket(&o->send_writer, data_len);
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
/**
|
||||||
|
* @file PeerChat.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_PEERCHAT_H
|
||||||
|
#define BADVPN_PEERCHAT_H
|
||||||
|
|
||||||
|
#include <cert.h>
|
||||||
|
#include <keyhi.h>
|
||||||
|
|
||||||
|
#include <protocol/packetproto.h>
|
||||||
|
#include <protocol/scproto.h>
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <misc/debugerror.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <base/BPending.h>
|
||||||
|
#include <base/BLog.h>
|
||||||
|
#include <flow/SinglePacketSender.h>
|
||||||
|
#include <flow/PacketProtoEncoder.h>
|
||||||
|
#include <flow/PacketCopier.h>
|
||||||
|
#include <flow/StreamPacketSender.h>
|
||||||
|
#include <flow/PacketStreamSender.h>
|
||||||
|
#include <flow/SinglePacketBuffer.h>
|
||||||
|
#include <flow/PacketProtoDecoder.h>
|
||||||
|
#include <flow/PacketBuffer.h>
|
||||||
|
#include <flow/BufferWriter.h>
|
||||||
|
#include <nspr_support/BSSLConnection.h>
|
||||||
|
#include <client/SCOutmsgEncoder.h>
|
||||||
|
#include <client/SimpleStreamBuffer.h>
|
||||||
|
|
||||||
|
#define PEERCHAT_SSL_NONE 0
|
||||||
|
#define PEERCHAT_SSL_CLIENT 1
|
||||||
|
#define PEERCHAT_SSL_SERVER 2
|
||||||
|
|
||||||
|
#define PEERCHAT_SSL_RECV_BUF_SIZE 4096
|
||||||
|
#define PEERCHAT_SEND_BUF_SIZE 200
|
||||||
|
|
||||||
|
//#define PEERCHAT_SIMULATE_ERROR 40
|
||||||
|
|
||||||
|
typedef void (*PeerChat_handler_error) (void *user);
|
||||||
|
typedef void (*PeerChat_handler_message) (void *user, uint8_t *data, int data_len);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int ssl_mode;
|
||||||
|
CERTCertificate *ssl_cert;
|
||||||
|
SECKEYPrivateKey *ssl_key;
|
||||||
|
uint8_t *ssl_peer_cert;
|
||||||
|
int ssl_peer_cert_len;
|
||||||
|
void *user;
|
||||||
|
BLog_logfunc logfunc;
|
||||||
|
PeerChat_handler_error handler_error;
|
||||||
|
PeerChat_handler_message handler_message;
|
||||||
|
|
||||||
|
// transport
|
||||||
|
PacketProtoEncoder pp_encoder;
|
||||||
|
SCOutmsgEncoder sc_encoder;
|
||||||
|
PacketCopier copier;
|
||||||
|
BPending recv_job;
|
||||||
|
uint8_t *recv_data;
|
||||||
|
int recv_data_len;
|
||||||
|
|
||||||
|
// SSL transport
|
||||||
|
StreamPacketSender ssl_sp_sender;
|
||||||
|
SimpleStreamBuffer ssl_recv_buf;
|
||||||
|
|
||||||
|
// SSL connection
|
||||||
|
PRFileDesc ssl_bottom_prfd;
|
||||||
|
PRFileDesc *ssl_prfd;
|
||||||
|
BSSLConnection ssl_con;
|
||||||
|
|
||||||
|
// SSL higher layer
|
||||||
|
PacketStreamSender ssl_ps_sender;
|
||||||
|
SinglePacketBuffer ssl_buffer;
|
||||||
|
PacketProtoEncoder ssl_encoder;
|
||||||
|
PacketCopier ssl_copier;
|
||||||
|
PacketProtoDecoder ssl_recv_decoder;
|
||||||
|
PacketPassInterface ssl_recv_if;
|
||||||
|
|
||||||
|
// higher layer send buffer
|
||||||
|
PacketBuffer send_buf;
|
||||||
|
BufferWriter send_writer;
|
||||||
|
|
||||||
|
DebugError d_err;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} PeerChat;
|
||||||
|
|
||||||
|
int PeerChat_Init (PeerChat *o, peerid_t peer_id, int ssl_mode, int ssl_flags, CERTCertificate *ssl_cert, SECKEYPrivateKey *ssl_key,
|
||||||
|
uint8_t *ssl_peer_cert, int ssl_peer_cert_len, BPendingGroup *pg, BThreadWorkDispatcher *twd, void *user,
|
||||||
|
BLog_logfunc logfunc,
|
||||||
|
PeerChat_handler_error handler_error,
|
||||||
|
PeerChat_handler_message handler_message) WARN_UNUSED;
|
||||||
|
void PeerChat_Free (PeerChat *o);
|
||||||
|
PacketRecvInterface * PeerChat_GetSendOutput (PeerChat *o);
|
||||||
|
void PeerChat_InputReceived (PeerChat *o, uint8_t *data, int data_len);
|
||||||
|
int PeerChat_StartMessage (PeerChat *o, uint8_t **data) WARN_UNUSED;
|
||||||
|
void PeerChat_EndMessage (PeerChat *o, int data_len);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,104 @@
|
||||||
|
/**
|
||||||
|
* @file SCOutmsgEncoder.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <misc/balign.h>
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <misc/byteorder.h>
|
||||||
|
|
||||||
|
#include "SCOutmsgEncoder.h"
|
||||||
|
|
||||||
|
static void output_handler_recv (SCOutmsgEncoder *o, uint8_t *data)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(!o->output_packet)
|
||||||
|
ASSERT(data)
|
||||||
|
|
||||||
|
// schedule receive
|
||||||
|
o->output_packet = data;
|
||||||
|
PacketRecvInterface_Receiver_Recv(o->input, o->output_packet + SCOUTMSG_OVERHEAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void input_handler_done (SCOutmsgEncoder *o, int in_len)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(o->output_packet)
|
||||||
|
|
||||||
|
// write SC header
|
||||||
|
struct sc_header header;
|
||||||
|
header.type = htol8(SCID_OUTMSG);
|
||||||
|
memcpy(o->output_packet, &header, sizeof(header));
|
||||||
|
|
||||||
|
// write outmsg
|
||||||
|
struct sc_client_outmsg outmsg;
|
||||||
|
outmsg.clientid = htol16(o->peer_id);
|
||||||
|
memcpy(o->output_packet + sizeof(header), &outmsg, sizeof(outmsg));
|
||||||
|
|
||||||
|
// finish output packet
|
||||||
|
o->output_packet = NULL;
|
||||||
|
PacketRecvInterface_Done(&o->output, SCOUTMSG_OVERHEAD + in_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SCOutmsgEncoder_Init (SCOutmsgEncoder *o, peerid_t peer_id, PacketRecvInterface *input, BPendingGroup *pg)
|
||||||
|
{
|
||||||
|
ASSERT(PacketRecvInterface_GetMTU(input) <= INT_MAX - SCOUTMSG_OVERHEAD)
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
o->peer_id = peer_id;
|
||||||
|
o->input = input;
|
||||||
|
|
||||||
|
// init input
|
||||||
|
PacketRecvInterface_Receiver_Init(o->input, (PacketRecvInterface_handler_done)input_handler_done, o);
|
||||||
|
|
||||||
|
// init output
|
||||||
|
PacketRecvInterface_Init(&o->output, SCOUTMSG_OVERHEAD + PacketRecvInterface_GetMTU(o->input), (PacketRecvInterface_handler_recv)output_handler_recv, o, pg);
|
||||||
|
|
||||||
|
// set no output packet
|
||||||
|
o->output_packet = NULL;
|
||||||
|
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SCOutmsgEncoder_Free (SCOutmsgEncoder *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
|
||||||
|
// free input
|
||||||
|
PacketRecvInterface_Free(&o->output);
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketRecvInterface * SCOutmsgEncoder_GetOutput (SCOutmsgEncoder *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
return &o->output;
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/**
|
||||||
|
* @file SCOutmsgEncoder.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_SCOUTMSGENCODER_H
|
||||||
|
#define BADVPN_SCOUTMSGENCODER_H
|
||||||
|
|
||||||
|
#include <protocol/scproto.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <flow/PacketRecvInterface.h>
|
||||||
|
|
||||||
|
#define SCOUTMSG_OVERHEAD (sizeof(struct sc_header) + sizeof(struct sc_client_outmsg))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link PacketRecvInterface} layer which encodes SCProto outgoing messages.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
peerid_t peer_id;
|
||||||
|
PacketRecvInterface *input;
|
||||||
|
PacketRecvInterface output;
|
||||||
|
uint8_t *output_packet;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} SCOutmsgEncoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the object.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param peer_id destination peer for messages
|
||||||
|
* @param input input interface. Its MTU muse be <= (INT_MAX - SCOUTMSG_OVERHEAD).
|
||||||
|
* @param pg pending group we live in
|
||||||
|
*/
|
||||||
|
void SCOutmsgEncoder_Init (SCOutmsgEncoder *o, peerid_t peer_id, PacketRecvInterface *input, BPendingGroup *pg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the object.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void SCOutmsgEncoder_Free (SCOutmsgEncoder *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the output interface.
|
||||||
|
* The MTU of the interface will be (SCOUTMSG_OVERHEAD + input MTU).
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @return output interface
|
||||||
|
*/
|
||||||
|
PacketRecvInterface * SCOutmsgEncoder_GetOutput (SCOutmsgEncoder *o);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,398 @@
|
||||||
|
/**
|
||||||
|
* @file SPProtoDecoder.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <misc/balign.h>
|
||||||
|
#include <misc/byteorder.h>
|
||||||
|
#include <security/BHash.h>
|
||||||
|
|
||||||
|
#include "SPProtoDecoder.h"
|
||||||
|
|
||||||
|
#include <generated/blog_channel_SPProtoDecoder.h>
|
||||||
|
|
||||||
|
#define PeerLog(_o, ...) BLog_LogViaFunc((_o)->logfunc, (_o)->user, BLOG_CURRENT_CHANNEL, __VA_ARGS__)
|
||||||
|
|
||||||
|
static void decode_work_func (SPProtoDecoder *o)
|
||||||
|
{
|
||||||
|
ASSERT(o->in_len >= 0)
|
||||||
|
ASSERT(o->in_len <= o->input_mtu)
|
||||||
|
|
||||||
|
uint8_t *in = o->in;
|
||||||
|
int in_len = o->in_len;
|
||||||
|
|
||||||
|
o->tw_out_len = -1;
|
||||||
|
|
||||||
|
uint8_t *plaintext;
|
||||||
|
int plaintext_len;
|
||||||
|
|
||||||
|
// decrypt if needed
|
||||||
|
if (!SPPROTO_HAVE_ENCRYPTION(o->sp_params)) {
|
||||||
|
plaintext = in;
|
||||||
|
plaintext_len = in_len;
|
||||||
|
} else {
|
||||||
|
// input must be a multiple of blocks size
|
||||||
|
if (in_len % o->enc_block_size != 0) {
|
||||||
|
PeerLog(o, BLOG_WARNING, "packet size not a multiple of block size");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// input must have an IV block
|
||||||
|
if (in_len < o->enc_block_size) {
|
||||||
|
PeerLog(o, BLOG_WARNING, "packet does not have an IV");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we have encryption key
|
||||||
|
if (!o->have_encryption_key) {
|
||||||
|
PeerLog(o, BLOG_WARNING, "have no encryption key");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy IV as BEncryption_Decrypt changes the IV
|
||||||
|
uint8_t iv[BENCRYPTION_MAX_BLOCK_SIZE];
|
||||||
|
memcpy(iv, in, o->enc_block_size);
|
||||||
|
|
||||||
|
// decrypt
|
||||||
|
uint8_t *ciphertext = in + o->enc_block_size;
|
||||||
|
int ciphertext_len = in_len - o->enc_block_size;
|
||||||
|
plaintext = o->buf;
|
||||||
|
BEncryption_Decrypt(&o->encryptor, ciphertext, plaintext, ciphertext_len, iv);
|
||||||
|
|
||||||
|
// read padding
|
||||||
|
if (ciphertext_len < o->enc_block_size) {
|
||||||
|
PeerLog(o, BLOG_WARNING, "packet does not have a padding block");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int i;
|
||||||
|
for (i = ciphertext_len - 1; i >= ciphertext_len - o->enc_block_size; i--) {
|
||||||
|
if (plaintext[i] == 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (plaintext[i] != 0) {
|
||||||
|
PeerLog(o, BLOG_WARNING, "packet padding wrong (nonzero byte)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i < ciphertext_len - o->enc_block_size) {
|
||||||
|
PeerLog(o, BLOG_WARNING, "packet padding wrong (all zeroes)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
plaintext_len = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for header
|
||||||
|
if (plaintext_len < SPPROTO_HEADER_LEN(o->sp_params)) {
|
||||||
|
PeerLog(o, BLOG_WARNING, "packet has no header");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t *header = plaintext;
|
||||||
|
|
||||||
|
// check data length
|
||||||
|
if (plaintext_len - SPPROTO_HEADER_LEN(o->sp_params) > o->output_mtu) {
|
||||||
|
PeerLog(o, BLOG_WARNING, "packet too long");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check OTP
|
||||||
|
if (SPPROTO_HAVE_OTP(o->sp_params)) {
|
||||||
|
// remember seed and OTP (can't check from here)
|
||||||
|
struct spproto_otpdata header_otpd;
|
||||||
|
memcpy(&header_otpd, header + SPPROTO_HEADER_OTPDATA_OFF(o->sp_params), sizeof(header_otpd));
|
||||||
|
o->tw_out_seed_id = ltoh16(header_otpd.seed_id);
|
||||||
|
o->tw_out_otp = header_otpd.otp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check hash
|
||||||
|
if (SPPROTO_HAVE_HASH(o->sp_params)) {
|
||||||
|
uint8_t *header_hash = header + SPPROTO_HEADER_HASH_OFF(o->sp_params);
|
||||||
|
// read hash
|
||||||
|
uint8_t hash[BHASH_MAX_SIZE];
|
||||||
|
memcpy(hash, header_hash, o->hash_size);
|
||||||
|
// zero hash in packet
|
||||||
|
memset(header_hash, 0, o->hash_size);
|
||||||
|
// calculate hash
|
||||||
|
uint8_t hash_calc[BHASH_MAX_SIZE];
|
||||||
|
BHash_calculate(o->sp_params.hash_mode, plaintext, plaintext_len, hash_calc);
|
||||||
|
// set hash field to its original value
|
||||||
|
memcpy(header_hash, hash, o->hash_size);
|
||||||
|
// compare hashes
|
||||||
|
if (memcmp(hash, hash_calc, o->hash_size)) {
|
||||||
|
PeerLog(o, BLOG_WARNING, "packet has wrong hash");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return packet
|
||||||
|
o->tw_out = plaintext + SPPROTO_HEADER_LEN(o->sp_params);
|
||||||
|
o->tw_out_len = plaintext_len - SPPROTO_HEADER_LEN(o->sp_params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void decode_work_handler (SPProtoDecoder *o)
|
||||||
|
{
|
||||||
|
ASSERT(o->in_len >= 0)
|
||||||
|
ASSERT(o->tw_have)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// free work
|
||||||
|
BThreadWork_Free(&o->tw);
|
||||||
|
o->tw_have = 0;
|
||||||
|
|
||||||
|
// check OTP
|
||||||
|
if (SPPROTO_HAVE_OTP(o->sp_params) && o->tw_out_len >= 0) {
|
||||||
|
if (!OTPChecker_CheckOTP(&o->otpchecker, o->tw_out_seed_id, o->tw_out_otp)) {
|
||||||
|
PeerLog(o, BLOG_WARNING, "packet has wrong OTP");
|
||||||
|
o->tw_out_len = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o->tw_out_len < 0) {
|
||||||
|
// cannot decode, finish input packet
|
||||||
|
PacketPassInterface_Done(&o->input);
|
||||||
|
o->in_len = -1;
|
||||||
|
} else {
|
||||||
|
// submit decoded packet to output
|
||||||
|
PacketPassInterface_Sender_Send(o->output, o->tw_out, o->tw_out_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void input_handler_send (SPProtoDecoder *o, uint8_t *data, int data_len)
|
||||||
|
{
|
||||||
|
ASSERT(data_len >= 0)
|
||||||
|
ASSERT(data_len <= o->input_mtu)
|
||||||
|
ASSERT(o->in_len == -1)
|
||||||
|
ASSERT(!o->tw_have)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// remember input
|
||||||
|
o->in = data;
|
||||||
|
o->in_len = data_len;
|
||||||
|
|
||||||
|
// start decoding
|
||||||
|
BThreadWork_Init(&o->tw, o->twd, (BThreadWork_handler_done)decode_work_handler, o, (BThreadWork_work_func)decode_work_func, o);
|
||||||
|
o->tw_have = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_handler_done (SPProtoDecoder *o)
|
||||||
|
{
|
||||||
|
ASSERT(o->in_len >= 0)
|
||||||
|
ASSERT(!o->tw_have)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// finish input packet
|
||||||
|
PacketPassInterface_Done(&o->input);
|
||||||
|
o->in_len = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void maybe_stop_work_and_ignore (SPProtoDecoder *o)
|
||||||
|
{
|
||||||
|
ASSERT(!(o->tw_have) || o->in_len >= 0)
|
||||||
|
|
||||||
|
if (o->tw_have) {
|
||||||
|
// free work
|
||||||
|
BThreadWork_Free(&o->tw);
|
||||||
|
o->tw_have = 0;
|
||||||
|
|
||||||
|
// ignore packet, receive next one
|
||||||
|
PacketPassInterface_Done(&o->input);
|
||||||
|
o->in_len = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int SPProtoDecoder_Init (SPProtoDecoder *o, PacketPassInterface *output, struct spproto_security_params sp_params, int num_otp_seeds, BPendingGroup *pg, BThreadWorkDispatcher *twd, void *user, BLog_logfunc logfunc)
|
||||||
|
{
|
||||||
|
spproto_assert_security_params(sp_params);
|
||||||
|
ASSERT(spproto_carrier_mtu_for_payload_mtu(sp_params, PacketPassInterface_GetMTU(output)) >= 0)
|
||||||
|
ASSERT(!SPPROTO_HAVE_OTP(sp_params) || num_otp_seeds >= 2)
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
o->output = output;
|
||||||
|
o->sp_params = sp_params;
|
||||||
|
o->twd = twd;
|
||||||
|
o->user = user;
|
||||||
|
o->logfunc = logfunc;
|
||||||
|
|
||||||
|
// init output
|
||||||
|
PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o);
|
||||||
|
|
||||||
|
// remember output MTU
|
||||||
|
o->output_mtu = PacketPassInterface_GetMTU(o->output);
|
||||||
|
|
||||||
|
// calculate hash size
|
||||||
|
if (SPPROTO_HAVE_HASH(o->sp_params)) {
|
||||||
|
o->hash_size = BHash_size(o->sp_params.hash_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate encryption block and key sizes
|
||||||
|
if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) {
|
||||||
|
o->enc_block_size = BEncryption_cipher_block_size(o->sp_params.encryption_mode);
|
||||||
|
o->enc_key_size = BEncryption_cipher_key_size(o->sp_params.encryption_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate input MTU
|
||||||
|
o->input_mtu = spproto_carrier_mtu_for_payload_mtu(o->sp_params, o->output_mtu);
|
||||||
|
|
||||||
|
// allocate plaintext buffer
|
||||||
|
if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) {
|
||||||
|
int buf_size = balign_up((SPPROTO_HEADER_LEN(o->sp_params) + o->output_mtu + 1), o->enc_block_size);
|
||||||
|
if (!(o->buf = (uint8_t *)malloc(buf_size))) {
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// init input
|
||||||
|
PacketPassInterface_Init(&o->input, o->input_mtu, (PacketPassInterface_handler_send)input_handler_send, o, pg);
|
||||||
|
|
||||||
|
// init OTP checker
|
||||||
|
if (SPPROTO_HAVE_OTP(o->sp_params)) {
|
||||||
|
if (!OTPChecker_Init(&o->otpchecker, o->sp_params.otp_num, o->sp_params.otp_mode, num_otp_seeds, o->twd)) {
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// have no encryption key
|
||||||
|
if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) {
|
||||||
|
o->have_encryption_key = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// have no input packet
|
||||||
|
o->in_len = -1;
|
||||||
|
|
||||||
|
// have no work
|
||||||
|
o->tw_have = 0;
|
||||||
|
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail1:
|
||||||
|
PacketPassInterface_Free(&o->input);
|
||||||
|
if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) {
|
||||||
|
free(o->buf);
|
||||||
|
}
|
||||||
|
fail0:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPProtoDecoder_Free (SPProtoDecoder *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
|
||||||
|
// free work
|
||||||
|
if (o->tw_have) {
|
||||||
|
BThreadWork_Free(&o->tw);
|
||||||
|
}
|
||||||
|
|
||||||
|
// free encryptor
|
||||||
|
if (SPPROTO_HAVE_ENCRYPTION(o->sp_params) && o->have_encryption_key) {
|
||||||
|
BEncryption_Free(&o->encryptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// free OTP checker
|
||||||
|
if (SPPROTO_HAVE_OTP(o->sp_params)) {
|
||||||
|
OTPChecker_Free(&o->otpchecker);
|
||||||
|
}
|
||||||
|
|
||||||
|
// free input
|
||||||
|
PacketPassInterface_Free(&o->input);
|
||||||
|
|
||||||
|
// free plaintext buffer
|
||||||
|
if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) {
|
||||||
|
free(o->buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketPassInterface * SPProtoDecoder_GetInput (SPProtoDecoder *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
return &o->input;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPProtoDecoder_SetEncryptionKey (SPProtoDecoder *o, uint8_t *encryption_key)
|
||||||
|
{
|
||||||
|
ASSERT(SPPROTO_HAVE_ENCRYPTION(o->sp_params))
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// stop existing work
|
||||||
|
maybe_stop_work_and_ignore(o);
|
||||||
|
|
||||||
|
// free encryptor
|
||||||
|
if (o->have_encryption_key) {
|
||||||
|
BEncryption_Free(&o->encryptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// init encryptor
|
||||||
|
BEncryption_Init(&o->encryptor, BENCRYPTION_MODE_DECRYPT, o->sp_params.encryption_mode, encryption_key);
|
||||||
|
|
||||||
|
// have encryption key
|
||||||
|
o->have_encryption_key = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPProtoDecoder_RemoveEncryptionKey (SPProtoDecoder *o)
|
||||||
|
{
|
||||||
|
ASSERT(SPPROTO_HAVE_ENCRYPTION(o->sp_params))
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// stop existing work
|
||||||
|
maybe_stop_work_and_ignore(o);
|
||||||
|
|
||||||
|
if (o->have_encryption_key) {
|
||||||
|
// free encryptor
|
||||||
|
BEncryption_Free(&o->encryptor);
|
||||||
|
|
||||||
|
// have no encryption key
|
||||||
|
o->have_encryption_key = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPProtoDecoder_AddOTPSeed (SPProtoDecoder *o, uint16_t seed_id, uint8_t *key, uint8_t *iv)
|
||||||
|
{
|
||||||
|
ASSERT(SPPROTO_HAVE_OTP(o->sp_params))
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
OTPChecker_AddSeed(&o->otpchecker, seed_id, key, iv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPProtoDecoder_RemoveOTPSeeds (SPProtoDecoder *o)
|
||||||
|
{
|
||||||
|
ASSERT(SPPROTO_HAVE_OTP(o->sp_params))
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
OTPChecker_RemoveSeeds(&o->otpchecker);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPProtoDecoder_SetHandlers (SPProtoDecoder *o, SPProtoDecoder_otp_handler otp_handler, void *user)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
if (SPPROTO_HAVE_OTP(o->sp_params)) {
|
||||||
|
OTPChecker_SetHandlers(&o->otpchecker, otp_handler, user);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,171 @@
|
||||||
|
/**
|
||||||
|
* @file SPProtoDecoder.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @section DESCRIPTION
|
||||||
|
*
|
||||||
|
* Object which decodes packets according to SPProto.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_CLIENT_SPPROTODECODER_H
|
||||||
|
#define BADVPN_CLIENT_SPPROTODECODER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <base/BLog.h>
|
||||||
|
#include <protocol/spproto.h>
|
||||||
|
#include <security/BEncryption.h>
|
||||||
|
#include <security/OTPChecker.h>
|
||||||
|
#include <flow/PacketPassInterface.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler called when OTP generation for a new seed is finished.
|
||||||
|
*
|
||||||
|
* @param user as in {@link SPProtoDecoder_Init}
|
||||||
|
*/
|
||||||
|
typedef void (*SPProtoDecoder_otp_handler) (void *user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object which decodes packets according to SPProto.
|
||||||
|
* Input is with {@link PacketPassInterface}.
|
||||||
|
* Output is with {@link PacketPassInterface}.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
PacketPassInterface *output;
|
||||||
|
struct spproto_security_params sp_params;
|
||||||
|
BThreadWorkDispatcher *twd;
|
||||||
|
void *user;
|
||||||
|
BLog_logfunc logfunc;
|
||||||
|
int output_mtu;
|
||||||
|
int hash_size;
|
||||||
|
int enc_block_size;
|
||||||
|
int enc_key_size;
|
||||||
|
int input_mtu;
|
||||||
|
uint8_t *buf;
|
||||||
|
PacketPassInterface input;
|
||||||
|
OTPChecker otpchecker;
|
||||||
|
int have_encryption_key;
|
||||||
|
BEncryption encryptor;
|
||||||
|
uint8_t *in;
|
||||||
|
int in_len;
|
||||||
|
int tw_have;
|
||||||
|
BThreadWork tw;
|
||||||
|
uint16_t tw_out_seed_id;
|
||||||
|
otp_t tw_out_otp;
|
||||||
|
uint8_t *tw_out;
|
||||||
|
int tw_out_len;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} SPProtoDecoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the object.
|
||||||
|
* {@link BSecurity_GlobalInitThreadSafe} must have been done if
|
||||||
|
* {@link BThreadWorkDispatcher_UsingThreads}(twd) = 1.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param output output interface. Its MTU must not be too large, i.e. this must hold:
|
||||||
|
* spproto_carrier_mtu_for_payload_mtu(sp_params, output MTU) >= 0
|
||||||
|
* @param sp_params SPProto parameters
|
||||||
|
* @param encryption_key if using encryption, the encryption key
|
||||||
|
* @param num_otp_seeds if using OTPs, how many OTP seeds to keep for checking
|
||||||
|
* receiving packets. Must be >=2 if using OTPs.
|
||||||
|
* @param pg pending group
|
||||||
|
* @param twd thread work dispatcher
|
||||||
|
* @param user argument to handlers
|
||||||
|
* @param logfunc function which prepends the log prefix using {@link BLog_Append}
|
||||||
|
* @return 1 on success, 0 on failure
|
||||||
|
*/
|
||||||
|
int SPProtoDecoder_Init (SPProtoDecoder *o, PacketPassInterface *output, struct spproto_security_params sp_params, int num_otp_seeds, BPendingGroup *pg, BThreadWorkDispatcher *twd, void *user, BLog_logfunc logfunc) WARN_UNUSED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the object.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void SPProtoDecoder_Free (SPProtoDecoder *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the input interface.
|
||||||
|
* The MTU of the input interface will depend on the output MTU and security parameters,
|
||||||
|
* that is spproto_carrier_mtu_for_payload_mtu(sp_params, output MTU).
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @return input interface
|
||||||
|
*/
|
||||||
|
PacketPassInterface * SPProtoDecoder_GetInput (SPProtoDecoder *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an encryption key for decrypting packets.
|
||||||
|
* Encryption must be enabled.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param encryption_key key to use
|
||||||
|
*/
|
||||||
|
void SPProtoDecoder_SetEncryptionKey (SPProtoDecoder *o, uint8_t *encryption_key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an encryption key if one is configured.
|
||||||
|
* Encryption must be enabled.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void SPProtoDecoder_RemoveEncryptionKey (SPProtoDecoder *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts generating OTPs for a seed to check received packets against.
|
||||||
|
* OTPs for this seed will not be recognized until the {@link SPProtoDecoder_otp_handler} handler
|
||||||
|
* is called.
|
||||||
|
* If OTPs are still being generated for the previous seed, it will be forgotten.
|
||||||
|
* OTPs must be enabled.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param seed_id seed identifier
|
||||||
|
* @param key OTP encryption key
|
||||||
|
* @param iv OTP initialization vector
|
||||||
|
*/
|
||||||
|
void SPProtoDecoder_AddOTPSeed (SPProtoDecoder *o, uint16_t seed_id, uint8_t *key, uint8_t *iv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all OTP seeds for checking received packets against.
|
||||||
|
* OTPs must be enabled.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void SPProtoDecoder_RemoveOTPSeeds (SPProtoDecoder *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets handlers.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param otp_handler handler called when OTP generation is finished
|
||||||
|
* @param user argument to handler
|
||||||
|
*/
|
||||||
|
void SPProtoDecoder_SetHandlers (SPProtoDecoder *o, SPProtoDecoder_otp_handler otp_handler, void *user);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,436 @@
|
||||||
|
/**
|
||||||
|
* @file SPProtoEncoder.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <misc/balign.h>
|
||||||
|
#include <misc/offset.h>
|
||||||
|
#include <misc/byteorder.h>
|
||||||
|
#include <security/BRandom.h>
|
||||||
|
#include <security/BHash.h>
|
||||||
|
|
||||||
|
#include "SPProtoEncoder.h"
|
||||||
|
|
||||||
|
static int can_encode (SPProtoEncoder *o);
|
||||||
|
static void encode_packet (SPProtoEncoder *o);
|
||||||
|
static void encode_work_func (SPProtoEncoder *o);
|
||||||
|
static void encode_work_handler (SPProtoEncoder *o);
|
||||||
|
static void maybe_encode (SPProtoEncoder *o);
|
||||||
|
static void output_handler_recv (SPProtoEncoder *o, uint8_t *data);
|
||||||
|
static void input_handler_done (SPProtoEncoder *o, int data_len);
|
||||||
|
static void handler_job_hander (SPProtoEncoder *o);
|
||||||
|
static void otpgenerator_handler (SPProtoEncoder *o);
|
||||||
|
static void maybe_stop_work (SPProtoEncoder *o);
|
||||||
|
|
||||||
|
static int can_encode (SPProtoEncoder *o)
|
||||||
|
{
|
||||||
|
ASSERT(o->in_len >= 0)
|
||||||
|
ASSERT(o->out_have)
|
||||||
|
ASSERT(!o->tw_have)
|
||||||
|
|
||||||
|
return (
|
||||||
|
(!SPPROTO_HAVE_OTP(o->sp_params) || OTPGenerator_GetPosition(&o->otpgen) < o->sp_params.otp_num) &&
|
||||||
|
(!SPPROTO_HAVE_ENCRYPTION(o->sp_params) || o->have_encryption_key)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void encode_packet (SPProtoEncoder *o)
|
||||||
|
{
|
||||||
|
ASSERT(o->in_len >= 0)
|
||||||
|
ASSERT(o->out_have)
|
||||||
|
ASSERT(!o->tw_have)
|
||||||
|
ASSERT(can_encode(o))
|
||||||
|
|
||||||
|
// generate OTP, remember seed ID
|
||||||
|
if (SPPROTO_HAVE_OTP(o->sp_params)) {
|
||||||
|
o->tw_seed_id = o->otpgen_seed_id;
|
||||||
|
o->tw_otp = OTPGenerator_GetOTP(&o->otpgen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// start work
|
||||||
|
BThreadWork_Init(&o->tw, o->twd, (BThreadWork_handler_done)encode_work_handler, o, (BThreadWork_work_func)encode_work_func, o);
|
||||||
|
o->tw_have = 1;
|
||||||
|
|
||||||
|
// schedule OTP warning handler
|
||||||
|
if (SPPROTO_HAVE_OTP(o->sp_params) && OTPGenerator_GetPosition(&o->otpgen) == o->otp_warning_count) {
|
||||||
|
BPending_Set(&o->handler_job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void encode_work_func (SPProtoEncoder *o)
|
||||||
|
{
|
||||||
|
ASSERT(o->in_len >= 0)
|
||||||
|
ASSERT(o->out_have)
|
||||||
|
ASSERT(!SPPROTO_HAVE_ENCRYPTION(o->sp_params) || o->have_encryption_key)
|
||||||
|
|
||||||
|
ASSERT(o->in_len <= o->input_mtu)
|
||||||
|
|
||||||
|
// determine plaintext location
|
||||||
|
uint8_t *plaintext = (SPPROTO_HAVE_ENCRYPTION(o->sp_params) ? o->buf : o->out);
|
||||||
|
|
||||||
|
// plaintext begins with header
|
||||||
|
uint8_t *header = plaintext;
|
||||||
|
|
||||||
|
// plaintext is header + payload
|
||||||
|
int plaintext_len = SPPROTO_HEADER_LEN(o->sp_params) + o->in_len;
|
||||||
|
|
||||||
|
// write OTP
|
||||||
|
if (SPPROTO_HAVE_OTP(o->sp_params)) {
|
||||||
|
struct spproto_otpdata header_otpd;
|
||||||
|
header_otpd.seed_id = htol16(o->tw_seed_id);
|
||||||
|
header_otpd.otp = o->tw_otp;
|
||||||
|
memcpy(header + SPPROTO_HEADER_OTPDATA_OFF(o->sp_params), &header_otpd, sizeof(header_otpd));
|
||||||
|
}
|
||||||
|
|
||||||
|
// write hash
|
||||||
|
if (SPPROTO_HAVE_HASH(o->sp_params)) {
|
||||||
|
uint8_t *header_hash = header + SPPROTO_HEADER_HASH_OFF(o->sp_params);
|
||||||
|
// zero hash field
|
||||||
|
memset(header_hash, 0, o->hash_size);
|
||||||
|
// calculate hash
|
||||||
|
uint8_t hash[BHASH_MAX_SIZE];
|
||||||
|
BHash_calculate(o->sp_params.hash_mode, plaintext, plaintext_len, hash);
|
||||||
|
// set hash field
|
||||||
|
memcpy(header_hash, hash, o->hash_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int out_len;
|
||||||
|
|
||||||
|
if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) {
|
||||||
|
// encrypting pad(header + payload)
|
||||||
|
int cyphertext_len = balign_up((plaintext_len + 1), o->enc_block_size);
|
||||||
|
|
||||||
|
// write padding
|
||||||
|
plaintext[plaintext_len] = 1;
|
||||||
|
for (int i = plaintext_len + 1; i < cyphertext_len; i++) {
|
||||||
|
plaintext[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate IV
|
||||||
|
BRandom_randomize(o->out, o->enc_block_size);
|
||||||
|
|
||||||
|
// copy IV because BEncryption_Encrypt changes the IV
|
||||||
|
uint8_t iv[BENCRYPTION_MAX_BLOCK_SIZE];
|
||||||
|
memcpy(iv, o->out, o->enc_block_size);
|
||||||
|
|
||||||
|
// encrypt
|
||||||
|
BEncryption_Encrypt(&o->encryptor, plaintext, o->out + o->enc_block_size, cyphertext_len, iv);
|
||||||
|
out_len = o->enc_block_size + cyphertext_len;
|
||||||
|
} else {
|
||||||
|
out_len = plaintext_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remember length
|
||||||
|
o->tw_out_len = out_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void encode_work_handler (SPProtoEncoder *o)
|
||||||
|
{
|
||||||
|
ASSERT(o->in_len >= 0)
|
||||||
|
ASSERT(o->out_have)
|
||||||
|
ASSERT(o->tw_have)
|
||||||
|
|
||||||
|
// free work
|
||||||
|
BThreadWork_Free(&o->tw);
|
||||||
|
o->tw_have = 0;
|
||||||
|
|
||||||
|
// finish packet
|
||||||
|
o->in_len = -1;
|
||||||
|
o->out_have = 0;
|
||||||
|
PacketRecvInterface_Done(&o->output, o->tw_out_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void maybe_encode (SPProtoEncoder *o)
|
||||||
|
{
|
||||||
|
if (o->in_len >= 0 && o->out_have && !o->tw_have && can_encode(o)) {
|
||||||
|
encode_packet(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_handler_recv (SPProtoEncoder *o, uint8_t *data)
|
||||||
|
{
|
||||||
|
ASSERT(o->in_len == -1)
|
||||||
|
ASSERT(!o->out_have)
|
||||||
|
ASSERT(!o->tw_have)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// remember output packet
|
||||||
|
o->out_have = 1;
|
||||||
|
o->out = data;
|
||||||
|
|
||||||
|
// determine plaintext location
|
||||||
|
uint8_t *plaintext = (SPPROTO_HAVE_ENCRYPTION(o->sp_params) ? o->buf : o->out);
|
||||||
|
|
||||||
|
// schedule receive
|
||||||
|
PacketRecvInterface_Receiver_Recv(o->input, plaintext + SPPROTO_HEADER_LEN(o->sp_params));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void input_handler_done (SPProtoEncoder *o, int data_len)
|
||||||
|
{
|
||||||
|
ASSERT(data_len >= 0)
|
||||||
|
ASSERT(data_len <= o->input_mtu)
|
||||||
|
ASSERT(o->in_len == -1)
|
||||||
|
ASSERT(o->out_have)
|
||||||
|
ASSERT(!o->tw_have)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// remember input packet
|
||||||
|
o->in_len = data_len;
|
||||||
|
|
||||||
|
// encode if possible
|
||||||
|
if (can_encode(o)) {
|
||||||
|
encode_packet(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handler_job_hander (SPProtoEncoder *o)
|
||||||
|
{
|
||||||
|
ASSERT(SPPROTO_HAVE_OTP(o->sp_params))
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
if (o->handler) {
|
||||||
|
o->handler(o->user);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void otpgenerator_handler (SPProtoEncoder *o)
|
||||||
|
{
|
||||||
|
ASSERT(SPPROTO_HAVE_OTP(o->sp_params))
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// remember seed ID
|
||||||
|
o->otpgen_seed_id = o->otpgen_pending_seed_id;
|
||||||
|
|
||||||
|
// possibly continue I/O
|
||||||
|
maybe_encode(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void maybe_stop_work (SPProtoEncoder *o)
|
||||||
|
{
|
||||||
|
// stop existing work
|
||||||
|
if (o->tw_have) {
|
||||||
|
BThreadWork_Free(&o->tw);
|
||||||
|
o->tw_have = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int SPProtoEncoder_Init (SPProtoEncoder *o, PacketRecvInterface *input, struct spproto_security_params sp_params, int otp_warning_count, BPendingGroup *pg, BThreadWorkDispatcher *twd)
|
||||||
|
{
|
||||||
|
spproto_assert_security_params(sp_params);
|
||||||
|
ASSERT(spproto_carrier_mtu_for_payload_mtu(sp_params, PacketRecvInterface_GetMTU(input)) >= 0)
|
||||||
|
if (SPPROTO_HAVE_OTP(sp_params)) {
|
||||||
|
ASSERT(otp_warning_count > 0)
|
||||||
|
ASSERT(otp_warning_count <= sp_params.otp_num)
|
||||||
|
}
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
o->input = input;
|
||||||
|
o->sp_params = sp_params;
|
||||||
|
o->otp_warning_count = otp_warning_count;
|
||||||
|
o->twd = twd;
|
||||||
|
|
||||||
|
// set no handlers
|
||||||
|
o->handler = NULL;
|
||||||
|
|
||||||
|
// calculate hash size
|
||||||
|
if (SPPROTO_HAVE_HASH(o->sp_params)) {
|
||||||
|
o->hash_size = BHash_size(o->sp_params.hash_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate encryption block and key sizes
|
||||||
|
if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) {
|
||||||
|
o->enc_block_size = BEncryption_cipher_block_size(o->sp_params.encryption_mode);
|
||||||
|
o->enc_key_size = BEncryption_cipher_key_size(o->sp_params.encryption_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// init otp generator
|
||||||
|
if (SPPROTO_HAVE_OTP(o->sp_params)) {
|
||||||
|
if (!OTPGenerator_Init(&o->otpgen, o->sp_params.otp_num, o->sp_params.otp_mode, o->twd, (OTPGenerator_handler)otpgenerator_handler, o)) {
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// have no encryption key
|
||||||
|
if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) {
|
||||||
|
o->have_encryption_key = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remember input MTU
|
||||||
|
o->input_mtu = PacketRecvInterface_GetMTU(o->input);
|
||||||
|
|
||||||
|
// calculate output MTU
|
||||||
|
o->output_mtu = spproto_carrier_mtu_for_payload_mtu(o->sp_params, o->input_mtu);
|
||||||
|
|
||||||
|
// init input
|
||||||
|
PacketRecvInterface_Receiver_Init(o->input, (PacketRecvInterface_handler_done)input_handler_done, o);
|
||||||
|
|
||||||
|
// have no input in buffer
|
||||||
|
o->in_len = -1;
|
||||||
|
|
||||||
|
// init output
|
||||||
|
PacketRecvInterface_Init(&o->output, o->output_mtu, (PacketRecvInterface_handler_recv)output_handler_recv, o, pg);
|
||||||
|
|
||||||
|
// have no output available
|
||||||
|
o->out_have = 0;
|
||||||
|
|
||||||
|
// allocate plaintext buffer
|
||||||
|
if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) {
|
||||||
|
int buf_size = balign_up((SPPROTO_HEADER_LEN(o->sp_params) + o->input_mtu + 1), o->enc_block_size);
|
||||||
|
if (!(o->buf = (uint8_t *)malloc(buf_size))) {
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// init handler job
|
||||||
|
BPending_Init(&o->handler_job, pg, (BPending_handler)handler_job_hander, o);
|
||||||
|
|
||||||
|
// have no work
|
||||||
|
o->tw_have = 0;
|
||||||
|
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail1:
|
||||||
|
PacketRecvInterface_Free(&o->output);
|
||||||
|
if (SPPROTO_HAVE_OTP(o->sp_params)) {
|
||||||
|
OTPGenerator_Free(&o->otpgen);
|
||||||
|
}
|
||||||
|
fail0:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPProtoEncoder_Free (SPProtoEncoder *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
|
||||||
|
// free work
|
||||||
|
if (o->tw_have) {
|
||||||
|
BThreadWork_Free(&o->tw);
|
||||||
|
}
|
||||||
|
|
||||||
|
// free handler job
|
||||||
|
BPending_Free(&o->handler_job);
|
||||||
|
|
||||||
|
// free plaintext buffer
|
||||||
|
if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) {
|
||||||
|
free(o->buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// free output
|
||||||
|
PacketRecvInterface_Free(&o->output);
|
||||||
|
|
||||||
|
// free encryptor
|
||||||
|
if (SPPROTO_HAVE_ENCRYPTION(o->sp_params) && o->have_encryption_key) {
|
||||||
|
BEncryption_Free(&o->encryptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// free otp generator
|
||||||
|
if (SPPROTO_HAVE_OTP(o->sp_params)) {
|
||||||
|
OTPGenerator_Free(&o->otpgen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketRecvInterface * SPProtoEncoder_GetOutput (SPProtoEncoder *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
return &o->output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPProtoEncoder_SetEncryptionKey (SPProtoEncoder *o, uint8_t *encryption_key)
|
||||||
|
{
|
||||||
|
ASSERT(SPPROTO_HAVE_ENCRYPTION(o->sp_params))
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// stop existing work
|
||||||
|
maybe_stop_work(o);
|
||||||
|
|
||||||
|
// free encryptor
|
||||||
|
if (o->have_encryption_key) {
|
||||||
|
BEncryption_Free(&o->encryptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// init encryptor
|
||||||
|
BEncryption_Init(&o->encryptor, BENCRYPTION_MODE_ENCRYPT, o->sp_params.encryption_mode, encryption_key);
|
||||||
|
|
||||||
|
// have encryption key
|
||||||
|
o->have_encryption_key = 1;
|
||||||
|
|
||||||
|
// possibly continue I/O
|
||||||
|
maybe_encode(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPProtoEncoder_RemoveEncryptionKey (SPProtoEncoder *o)
|
||||||
|
{
|
||||||
|
ASSERT(SPPROTO_HAVE_ENCRYPTION(o->sp_params))
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// stop existing work
|
||||||
|
maybe_stop_work(o);
|
||||||
|
|
||||||
|
if (o->have_encryption_key) {
|
||||||
|
// free encryptor
|
||||||
|
BEncryption_Free(&o->encryptor);
|
||||||
|
|
||||||
|
// have no encryption key
|
||||||
|
o->have_encryption_key = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPProtoEncoder_SetOTPSeed (SPProtoEncoder *o, uint16_t seed_id, uint8_t *key, uint8_t *iv)
|
||||||
|
{
|
||||||
|
ASSERT(SPPROTO_HAVE_OTP(o->sp_params))
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// give seed to OTP generator
|
||||||
|
OTPGenerator_SetSeed(&o->otpgen, key, iv);
|
||||||
|
|
||||||
|
// remember seed ID
|
||||||
|
o->otpgen_pending_seed_id = seed_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPProtoEncoder_RemoveOTPSeed (SPProtoEncoder *o)
|
||||||
|
{
|
||||||
|
ASSERT(SPPROTO_HAVE_OTP(o->sp_params))
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// reset OTP generator
|
||||||
|
OTPGenerator_Reset(&o->otpgen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPProtoEncoder_SetHandlers (SPProtoEncoder *o, SPProtoEncoder_handler handler, void *user)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
o->handler = handler;
|
||||||
|
o->user = user;
|
||||||
|
}
|
|
@ -0,0 +1,172 @@
|
||||||
|
/**
|
||||||
|
* @file SPProtoEncoder.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @section DESCRIPTION
|
||||||
|
*
|
||||||
|
* Object which encodes packets according to SPProto.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_CLIENT_SPPROTOENCODER_H
|
||||||
|
#define BADVPN_CLIENT_SPPROTOENCODER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <protocol/spproto.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <security/BEncryption.h>
|
||||||
|
#include <security/OTPGenerator.h>
|
||||||
|
#include <flow/PacketRecvInterface.h>
|
||||||
|
#include <threadwork/BThreadWork.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event context handler called when the remaining number of
|
||||||
|
* OTPs equals the warning number after having encoded a packet.
|
||||||
|
*
|
||||||
|
* @param user as in {@link SPProtoEncoder_Init}
|
||||||
|
*/
|
||||||
|
typedef void (*SPProtoEncoder_handler) (void *user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object which encodes packets according to SPProto.
|
||||||
|
*
|
||||||
|
* Input is with {@link PacketRecvInterface}.
|
||||||
|
* Output is with {@link PacketRecvInterface}.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
PacketRecvInterface *input;
|
||||||
|
struct spproto_security_params sp_params;
|
||||||
|
int otp_warning_count;
|
||||||
|
SPProtoEncoder_handler handler;
|
||||||
|
BThreadWorkDispatcher *twd;
|
||||||
|
void *user;
|
||||||
|
int hash_size;
|
||||||
|
int enc_block_size;
|
||||||
|
int enc_key_size;
|
||||||
|
OTPGenerator otpgen;
|
||||||
|
uint16_t otpgen_seed_id;
|
||||||
|
uint16_t otpgen_pending_seed_id;
|
||||||
|
int have_encryption_key;
|
||||||
|
BEncryption encryptor;
|
||||||
|
int input_mtu;
|
||||||
|
int output_mtu;
|
||||||
|
int in_len;
|
||||||
|
PacketRecvInterface output;
|
||||||
|
int out_have;
|
||||||
|
uint8_t *out;
|
||||||
|
uint8_t *buf;
|
||||||
|
BPending handler_job;
|
||||||
|
int tw_have;
|
||||||
|
BThreadWork tw;
|
||||||
|
uint16_t tw_seed_id;
|
||||||
|
otp_t tw_otp;
|
||||||
|
int tw_out_len;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} SPProtoEncoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the object.
|
||||||
|
* The object is initialized in blocked state.
|
||||||
|
* {@link BSecurity_GlobalInitThreadSafe} must have been done if
|
||||||
|
* {@link BThreadWorkDispatcher_UsingThreads}(twd) = 1.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param input input interface. Its MTU must not be too large, i.e. this must hold:
|
||||||
|
* spproto_carrier_mtu_for_payload_mtu(sp_params, input MTU) >= 0
|
||||||
|
* @param sp_params SPProto security parameters
|
||||||
|
* @param otp_warning_count If using OTPs, after how many encoded packets to call the handler.
|
||||||
|
* In this case, must be >0 and <=sp_params.otp_num.
|
||||||
|
* @param pg pending group
|
||||||
|
* @param twd thread work dispatcher
|
||||||
|
* @return 1 on success, 0 on failure
|
||||||
|
*/
|
||||||
|
int SPProtoEncoder_Init (SPProtoEncoder *o, PacketRecvInterface *input, struct spproto_security_params sp_params, int otp_warning_count, BPendingGroup *pg, BThreadWorkDispatcher *twd) WARN_UNUSED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the object.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void SPProtoEncoder_Free (SPProtoEncoder *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the output interface.
|
||||||
|
* The MTU of the output interface will depend on the input MTU and security parameters,
|
||||||
|
* that is spproto_carrier_mtu_for_payload_mtu(sp_params, input MTU).
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @return output interface
|
||||||
|
*/
|
||||||
|
PacketRecvInterface * SPProtoEncoder_GetOutput (SPProtoEncoder *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an encryption key to use.
|
||||||
|
* Encryption must be enabled.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param encryption_key key to use
|
||||||
|
*/
|
||||||
|
void SPProtoEncoder_SetEncryptionKey (SPProtoEncoder *o, uint8_t *encryption_key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an encryption key if one is configured.
|
||||||
|
* Encryption must be enabled.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void SPProtoEncoder_RemoveEncryptionKey (SPProtoEncoder *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an OTP seed to use.
|
||||||
|
* OTPs must be enabled.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param seed_id seed identifier
|
||||||
|
* @param key OTP encryption key
|
||||||
|
* @param iv OTP initialization vector
|
||||||
|
*/
|
||||||
|
void SPProtoEncoder_SetOTPSeed (SPProtoEncoder *o, uint16_t seed_id, uint8_t *key, uint8_t *iv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the OTP seed if one is configured.
|
||||||
|
* OTPs must be enabled.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void SPProtoEncoder_RemoveOTPSeed (SPProtoEncoder *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets handlers.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param handler OTP warning handler
|
||||||
|
* @param user value to pass to handler
|
||||||
|
*/
|
||||||
|
void SPProtoEncoder_SetHandlers (SPProtoEncoder *o, SPProtoEncoder_handler handler, void *user);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,144 @@
|
||||||
|
/**
|
||||||
|
* @file SimpleStreamBuffer.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <misc/balloc.h>
|
||||||
|
#include <misc/minmax.h>
|
||||||
|
|
||||||
|
#include "SimpleStreamBuffer.h"
|
||||||
|
|
||||||
|
static void try_output (SimpleStreamBuffer *o)
|
||||||
|
{
|
||||||
|
ASSERT(o->output_data_len > 0)
|
||||||
|
|
||||||
|
// calculate number of bytes to output
|
||||||
|
int bytes = bmin_int(o->output_data_len, o->buf_used);
|
||||||
|
if (bytes == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy bytes to output
|
||||||
|
memcpy(o->output_data, o->buf, bytes);
|
||||||
|
|
||||||
|
// shift buffer
|
||||||
|
memmove(o->buf, o->buf + bytes, o->buf_used - bytes);
|
||||||
|
o->buf_used -= bytes;
|
||||||
|
|
||||||
|
// forget data
|
||||||
|
o->output_data_len = -1;
|
||||||
|
|
||||||
|
// done
|
||||||
|
StreamRecvInterface_Done(&o->output, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_handler_recv (SimpleStreamBuffer *o, uint8_t *data, int data_len)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(o->output_data_len == -1)
|
||||||
|
ASSERT(data)
|
||||||
|
ASSERT(data_len > 0)
|
||||||
|
|
||||||
|
// remember data
|
||||||
|
o->output_data = data;
|
||||||
|
o->output_data_len = data_len;
|
||||||
|
|
||||||
|
try_output(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
int SimpleStreamBuffer_Init (SimpleStreamBuffer *o, int buf_size, BPendingGroup *pg)
|
||||||
|
{
|
||||||
|
ASSERT(buf_size > 0)
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
o->buf_size = buf_size;
|
||||||
|
|
||||||
|
// init output
|
||||||
|
StreamRecvInterface_Init(&o->output, (StreamRecvInterface_handler_recv)output_handler_recv, o, pg);
|
||||||
|
|
||||||
|
// allocate buffer
|
||||||
|
if (!(o->buf = (uint8_t *)BAlloc(buf_size))) {
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init buffer state
|
||||||
|
o->buf_used = 0;
|
||||||
|
|
||||||
|
// set no output data
|
||||||
|
o->output_data_len = -1;
|
||||||
|
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail1:
|
||||||
|
StreamRecvInterface_Free(&o->output);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimpleStreamBuffer_Free (SimpleStreamBuffer *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
|
||||||
|
// free buffer
|
||||||
|
BFree(o->buf);
|
||||||
|
|
||||||
|
// free output
|
||||||
|
StreamRecvInterface_Free(&o->output);
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamRecvInterface * SimpleStreamBuffer_GetOutput (SimpleStreamBuffer *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
return &o->output;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SimpleStreamBuffer_Write (SimpleStreamBuffer *o, uint8_t *data, int data_len)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(data_len >= 0)
|
||||||
|
|
||||||
|
if (data_len > o->buf_size - o->buf_used) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy to buffer
|
||||||
|
memcpy(o->buf + o->buf_used, data, data_len);
|
||||||
|
|
||||||
|
// update buffer state
|
||||||
|
o->buf_used += data_len;
|
||||||
|
|
||||||
|
// continue outputting
|
||||||
|
if (o->output_data_len > 0) {
|
||||||
|
try_output(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/**
|
||||||
|
* @file SimpleStreamBuffer.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_SIMPLESTREAMBUFFER_H
|
||||||
|
#define BADVPN_SIMPLESTREAMBUFFER_H
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <flow/StreamRecvInterface.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int buf_size;
|
||||||
|
StreamRecvInterface output;
|
||||||
|
uint8_t *buf;
|
||||||
|
int buf_used;
|
||||||
|
uint8_t *output_data;
|
||||||
|
int output_data_len;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} SimpleStreamBuffer;
|
||||||
|
|
||||||
|
int SimpleStreamBuffer_Init (SimpleStreamBuffer *o, int buf_size, BPendingGroup *pg) WARN_UNUSED;
|
||||||
|
void SimpleStreamBuffer_Free (SimpleStreamBuffer *o);
|
||||||
|
StreamRecvInterface * SimpleStreamBuffer_GetOutput (SimpleStreamBuffer *o);
|
||||||
|
int SimpleStreamBuffer_Write (SimpleStreamBuffer *o, uint8_t *data, int data_len) WARN_UNUSED;
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,85 @@
|
||||||
|
/**
|
||||||
|
* @file SinglePacketSource.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
|
||||||
|
#include "SinglePacketSource.h"
|
||||||
|
|
||||||
|
static void output_handler_recv (SinglePacketSource *o, uint8_t *data)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// if we already sent one packet, stop
|
||||||
|
if (o->sent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set sent
|
||||||
|
o->sent = 1;
|
||||||
|
|
||||||
|
// write packet
|
||||||
|
memcpy(data, o->packet, o->packet_len);
|
||||||
|
|
||||||
|
// done
|
||||||
|
PacketRecvInterface_Done(&o->output, o->packet_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SinglePacketSource_Init (SinglePacketSource *o, uint8_t *packet, int packet_len, BPendingGroup *pg)
|
||||||
|
{
|
||||||
|
ASSERT(packet_len >= 0)
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
o->packet = packet;
|
||||||
|
o->packet_len = packet_len;
|
||||||
|
|
||||||
|
// set not sent
|
||||||
|
o->sent = 0;
|
||||||
|
|
||||||
|
// init output
|
||||||
|
PacketRecvInterface_Init(&o->output, o->packet_len, (PacketRecvInterface_handler_recv)output_handler_recv, o, pg);
|
||||||
|
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SinglePacketSource_Free (SinglePacketSource *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
|
||||||
|
// free output
|
||||||
|
PacketRecvInterface_Free(&o->output);
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketRecvInterface * SinglePacketSource_GetOutput (SinglePacketSource *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
return &o->output;
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
/**
|
||||||
|
* @file SinglePacketSource.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_SINGLEPACKETSOURCE_H
|
||||||
|
#define BADVPN_SINGLEPACKETSOURCE_H
|
||||||
|
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <flow/PacketRecvInterface.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object which provides a single packet through {@link PacketRecvInterface}.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint8_t *packet;
|
||||||
|
int packet_len;
|
||||||
|
int sent;
|
||||||
|
PacketRecvInterface output;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} SinglePacketSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the object.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @param packet packet to provide to the output. Must stay available until the packet is provided.
|
||||||
|
* @param packet_len length of packet. Must be >=0.
|
||||||
|
* @param pg pending group we live in
|
||||||
|
*/
|
||||||
|
void SinglePacketSource_Init (SinglePacketSource *o, uint8_t *packet, int packet_len, BPendingGroup *pg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the object.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
*/
|
||||||
|
void SinglePacketSource_Free (SinglePacketSource *o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the output interface.
|
||||||
|
* The MTU of the interface will be packet_len.
|
||||||
|
*
|
||||||
|
* @param o the object
|
||||||
|
* @return output interface
|
||||||
|
*/
|
||||||
|
PacketRecvInterface * SinglePacketSource_GetOutput (SinglePacketSource *o);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,712 @@
|
||||||
|
/**
|
||||||
|
* @file StreamPeerIO.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <ssl.h>
|
||||||
|
#include <sslerr.h>
|
||||||
|
|
||||||
|
#include <misc/offset.h>
|
||||||
|
#include <misc/byteorder.h>
|
||||||
|
|
||||||
|
#include <client/StreamPeerIO.h>
|
||||||
|
|
||||||
|
#include <generated/blog_channel_StreamPeerIO.h>
|
||||||
|
|
||||||
|
#define MODE_NONE 0
|
||||||
|
#define MODE_CONNECT 1
|
||||||
|
#define MODE_LISTEN 2
|
||||||
|
|
||||||
|
#define CONNECT_STATE_CONNECTING 0
|
||||||
|
#define CONNECT_STATE_HANDSHAKE 1
|
||||||
|
#define CONNECT_STATE_SENDING 2
|
||||||
|
#define CONNECT_STATE_SENT 3
|
||||||
|
#define CONNECT_STATE_FINISHED 4
|
||||||
|
|
||||||
|
#define LISTEN_STATE_LISTENER 0
|
||||||
|
#define LISTEN_STATE_GOTCLIENT 1
|
||||||
|
#define LISTEN_STATE_FINISHED 2
|
||||||
|
|
||||||
|
#define PeerLog(_o, ...) BLog_LogViaFunc((_o)->logfunc, (_o)->user, BLOG_CURRENT_CHANNEL, __VA_ARGS__)
|
||||||
|
|
||||||
|
static void decoder_handler_error (StreamPeerIO *pio);
|
||||||
|
static void connector_handler (StreamPeerIO *pio, int is_error);
|
||||||
|
static void connection_handler (StreamPeerIO *pio, int event);
|
||||||
|
static void connect_sslcon_handler (StreamPeerIO *pio, int event);
|
||||||
|
static void pwsender_handler (StreamPeerIO *pio);
|
||||||
|
static void listener_handler_client (StreamPeerIO *pio, sslsocket *sock);
|
||||||
|
static int init_io (StreamPeerIO *pio, sslsocket *sock);
|
||||||
|
static void free_io (StreamPeerIO *pio);
|
||||||
|
static void sslcon_handler (StreamPeerIO *pio, int event);
|
||||||
|
static SECStatus client_auth_certificate_callback (StreamPeerIO *pio, PRFileDesc *fd, PRBool checkSig, PRBool isServer);
|
||||||
|
static SECStatus client_client_auth_data_callback (StreamPeerIO *pio, PRFileDesc *fd, CERTDistNames *caNames, CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey);
|
||||||
|
static int compare_certificate (StreamPeerIO *pio, CERTCertificate *cert);
|
||||||
|
static void reset_state (StreamPeerIO *pio);
|
||||||
|
static void reset_and_report_error (StreamPeerIO *pio);
|
||||||
|
|
||||||
|
void decoder_handler_error (StreamPeerIO *pio)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&pio->d_obj);
|
||||||
|
|
||||||
|
PeerLog(pio, BLOG_ERROR, "decoder error");
|
||||||
|
|
||||||
|
reset_and_report_error(pio);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void connector_handler (StreamPeerIO *pio, int is_error)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&pio->d_obj);
|
||||||
|
ASSERT(pio->mode == MODE_CONNECT)
|
||||||
|
ASSERT(pio->connect.state == CONNECT_STATE_CONNECTING)
|
||||||
|
|
||||||
|
// check connection result
|
||||||
|
if (is_error) {
|
||||||
|
PeerLog(pio, BLOG_NOTICE, "connection failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init connection
|
||||||
|
if (!BConnection_Init(&pio->connect.sock.con, BConnection_source_connector(&pio->connect.connector), pio->reactor, pio, (BConnection_handler)connection_handler)) {
|
||||||
|
PeerLog(pio, BLOG_ERROR, "BConnection_Init failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pio->ssl) {
|
||||||
|
// init connection interfaces
|
||||||
|
BConnection_SendAsync_Init(&pio->connect.sock.con);
|
||||||
|
BConnection_RecvAsync_Init(&pio->connect.sock.con);
|
||||||
|
|
||||||
|
// create bottom NSPR file descriptor
|
||||||
|
if (!BSSLConnection_MakeBackend(&pio->connect.sock.bottom_prfd, BConnection_SendAsync_GetIf(&pio->connect.sock.con), BConnection_RecvAsync_GetIf(&pio->connect.sock.con), pio->twd, pio->ssl_flags)) {
|
||||||
|
PeerLog(pio, BLOG_ERROR, "BSSLConnection_MakeBackend failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create SSL file descriptor from the bottom NSPR file descriptor
|
||||||
|
if (!(pio->connect.sock.ssl_prfd = SSL_ImportFD(NULL, &pio->connect.sock.bottom_prfd))) {
|
||||||
|
ASSERT_FORCE(PR_Close(&pio->connect.sock.bottom_prfd) == PR_SUCCESS)
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set client mode
|
||||||
|
if (SSL_ResetHandshake(pio->connect.sock.ssl_prfd, PR_FALSE) != SECSuccess) {
|
||||||
|
PeerLog(pio, BLOG_ERROR, "SSL_ResetHandshake failed");
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set verify peer certificate hook
|
||||||
|
if (SSL_AuthCertificateHook(pio->connect.sock.ssl_prfd, (SSLAuthCertificate)client_auth_certificate_callback, pio) != SECSuccess) {
|
||||||
|
PeerLog(pio, BLOG_ERROR, "SSL_AuthCertificateHook failed");
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set client certificate callback
|
||||||
|
if (SSL_GetClientAuthDataHook(pio->connect.sock.ssl_prfd, (SSLGetClientAuthData)client_client_auth_data_callback, pio) != SECSuccess) {
|
||||||
|
PeerLog(pio, BLOG_ERROR, "SSL_GetClientAuthDataHook failed");
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init BSSLConnection
|
||||||
|
BSSLConnection_Init(&pio->connect.sslcon, pio->connect.sock.ssl_prfd, 1, BReactor_PendingGroup(pio->reactor), pio, (BSSLConnection_handler)connect_sslcon_handler);
|
||||||
|
|
||||||
|
// change state
|
||||||
|
pio->connect.state = CONNECT_STATE_HANDSHAKE;
|
||||||
|
} else {
|
||||||
|
// init connection send interface
|
||||||
|
BConnection_SendAsync_Init(&pio->connect.sock.con);
|
||||||
|
|
||||||
|
// init password sender
|
||||||
|
SingleStreamSender_Init(&pio->connect.pwsender, (uint8_t *)&pio->connect.password, sizeof(pio->connect.password), BConnection_SendAsync_GetIf(&pio->connect.sock.con), BReactor_PendingGroup(pio->reactor), pio, (SingleStreamSender_handler)pwsender_handler);
|
||||||
|
|
||||||
|
// change state
|
||||||
|
pio->connect.state = CONNECT_STATE_SENDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (pio->ssl) {
|
||||||
|
fail2:
|
||||||
|
ASSERT_FORCE(PR_Close(pio->connect.sock.ssl_prfd) == PR_SUCCESS)
|
||||||
|
fail1:
|
||||||
|
BConnection_RecvAsync_Free(&pio->connect.sock.con);
|
||||||
|
BConnection_SendAsync_Free(&pio->connect.sock.con);
|
||||||
|
}
|
||||||
|
BConnection_Free(&pio->connect.sock.con);
|
||||||
|
fail0:
|
||||||
|
reset_and_report_error(pio);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void connection_handler (StreamPeerIO *pio, int event)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&pio->d_obj);
|
||||||
|
ASSERT(pio->mode == MODE_CONNECT || pio->mode == MODE_LISTEN)
|
||||||
|
ASSERT(!(pio->mode == MODE_CONNECT) || pio->connect.state >= CONNECT_STATE_HANDSHAKE)
|
||||||
|
ASSERT(!(pio->mode == MODE_LISTEN) || pio->listen.state >= LISTEN_STATE_FINISHED)
|
||||||
|
|
||||||
|
if (event == BCONNECTION_EVENT_RECVCLOSED) {
|
||||||
|
PeerLog(pio, BLOG_NOTICE, "connection closed");
|
||||||
|
} else {
|
||||||
|
PeerLog(pio, BLOG_NOTICE, "connection error");
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_and_report_error(pio);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void connect_sslcon_handler (StreamPeerIO *pio, int event)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&pio->d_obj);
|
||||||
|
ASSERT(pio->ssl)
|
||||||
|
ASSERT(pio->mode == MODE_CONNECT)
|
||||||
|
ASSERT(pio->connect.state == CONNECT_STATE_HANDSHAKE || pio->connect.state == CONNECT_STATE_SENDING)
|
||||||
|
ASSERT(event == BSSLCONNECTION_EVENT_UP || event == BSSLCONNECTION_EVENT_ERROR)
|
||||||
|
|
||||||
|
if (event == BSSLCONNECTION_EVENT_ERROR) {
|
||||||
|
PeerLog(pio, BLOG_NOTICE, "SSL error");
|
||||||
|
|
||||||
|
reset_and_report_error(pio);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handshake complete
|
||||||
|
ASSERT(pio->connect.state == CONNECT_STATE_HANDSHAKE)
|
||||||
|
|
||||||
|
// remove client certificate callback
|
||||||
|
if (SSL_GetClientAuthDataHook(pio->connect.sock.ssl_prfd, NULL, NULL) != SECSuccess) {
|
||||||
|
PeerLog(pio, BLOG_ERROR, "SSL_GetClientAuthDataHook failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove verify peer certificate callback
|
||||||
|
if (SSL_AuthCertificateHook(pio->connect.sock.ssl_prfd, NULL, NULL) != SECSuccess) {
|
||||||
|
PeerLog(pio, BLOG_ERROR, "SSL_AuthCertificateHook failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init password sender
|
||||||
|
SingleStreamSender_Init(&pio->connect.pwsender, (uint8_t *)&pio->connect.password, sizeof(pio->connect.password), BSSLConnection_GetSendIf(&pio->connect.sslcon), BReactor_PendingGroup(pio->reactor), pio, (SingleStreamSender_handler)pwsender_handler);
|
||||||
|
|
||||||
|
// change state
|
||||||
|
pio->connect.state = CONNECT_STATE_SENDING;
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
fail0:
|
||||||
|
reset_and_report_error(pio);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pwsender_handler (StreamPeerIO *pio)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&pio->d_obj);
|
||||||
|
ASSERT(pio->mode == MODE_CONNECT)
|
||||||
|
ASSERT(pio->connect.state == CONNECT_STATE_SENDING)
|
||||||
|
|
||||||
|
// stop using any buffers before they get freed
|
||||||
|
if (pio->ssl) {
|
||||||
|
BSSLConnection_ReleaseBuffers(&pio->connect.sslcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
// free password sender
|
||||||
|
SingleStreamSender_Free(&pio->connect.pwsender);
|
||||||
|
|
||||||
|
if (pio->ssl) {
|
||||||
|
// free BSSLConnection (we used the send interface)
|
||||||
|
BSSLConnection_Free(&pio->connect.sslcon);
|
||||||
|
} else {
|
||||||
|
// init connection send interface
|
||||||
|
BConnection_SendAsync_Free(&pio->connect.sock.con);
|
||||||
|
}
|
||||||
|
|
||||||
|
// change state
|
||||||
|
pio->connect.state = CONNECT_STATE_SENT;
|
||||||
|
|
||||||
|
// setup i/o
|
||||||
|
if (!init_io(pio, &pio->connect.sock)) {
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// change state
|
||||||
|
pio->connect.state = CONNECT_STATE_FINISHED;
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
fail0:
|
||||||
|
reset_and_report_error(pio);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void listener_handler_client (StreamPeerIO *pio, sslsocket *sock)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&pio->d_obj);
|
||||||
|
ASSERT(pio->mode == MODE_LISTEN)
|
||||||
|
ASSERT(pio->listen.state == LISTEN_STATE_LISTENER)
|
||||||
|
|
||||||
|
// remember socket
|
||||||
|
pio->listen.sock = sock;
|
||||||
|
|
||||||
|
// set connection handler
|
||||||
|
BConnection_SetHandlers(&pio->listen.sock->con, pio, (BConnection_handler)connection_handler);
|
||||||
|
|
||||||
|
// change state
|
||||||
|
pio->listen.state = LISTEN_STATE_GOTCLIENT;
|
||||||
|
|
||||||
|
// check ceritficate
|
||||||
|
if (pio->ssl) {
|
||||||
|
CERTCertificate *peer_cert = SSL_PeerCertificate(pio->listen.sock->ssl_prfd);
|
||||||
|
if (!peer_cert) {
|
||||||
|
PeerLog(pio, BLOG_ERROR, "SSL_PeerCertificate failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare certificate to the one provided by the server
|
||||||
|
if (!compare_certificate(pio, peer_cert)) {
|
||||||
|
CERT_DestroyCertificate(peer_cert);
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CERT_DestroyCertificate(peer_cert);
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup i/o
|
||||||
|
if (!init_io(pio, pio->listen.sock)) {
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// change state
|
||||||
|
pio->listen.state = LISTEN_STATE_FINISHED;
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
fail0:
|
||||||
|
reset_and_report_error(pio);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int init_io (StreamPeerIO *pio, sslsocket *sock)
|
||||||
|
{
|
||||||
|
ASSERT(!pio->sock)
|
||||||
|
|
||||||
|
// limit socket send buffer, else our scheduling is pointless
|
||||||
|
if (pio->sock_sndbuf > 0) {
|
||||||
|
if (!BConnection_SetSendBuffer(&sock->con, pio->sock_sndbuf)) {
|
||||||
|
PeerLog(pio, BLOG_WARNING, "BConnection_SetSendBuffer failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pio->ssl) {
|
||||||
|
// init BSSLConnection
|
||||||
|
BSSLConnection_Init(&pio->sslcon, sock->ssl_prfd, 0, BReactor_PendingGroup(pio->reactor), pio, (BSSLConnection_handler)sslcon_handler);
|
||||||
|
} else {
|
||||||
|
// init connection interfaces
|
||||||
|
BConnection_SendAsync_Init(&sock->con);
|
||||||
|
BConnection_RecvAsync_Init(&sock->con);
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamPassInterface *send_if = (pio->ssl ? BSSLConnection_GetSendIf(&pio->sslcon) : BConnection_SendAsync_GetIf(&sock->con));
|
||||||
|
StreamRecvInterface *recv_if = (pio->ssl ? BSSLConnection_GetRecvIf(&pio->sslcon) : BConnection_RecvAsync_GetIf(&sock->con));
|
||||||
|
|
||||||
|
// init receiving
|
||||||
|
StreamRecvConnector_ConnectInput(&pio->input_connector, recv_if);
|
||||||
|
|
||||||
|
// init sending
|
||||||
|
PacketStreamSender_Init(&pio->output_pss, send_if, PACKETPROTO_ENCLEN(pio->payload_mtu), BReactor_PendingGroup(pio->reactor));
|
||||||
|
PacketPassConnector_ConnectOutput(&pio->output_connector, PacketStreamSender_GetInput(&pio->output_pss));
|
||||||
|
|
||||||
|
pio->sock = sock;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_io (StreamPeerIO *pio)
|
||||||
|
{
|
||||||
|
ASSERT(pio->sock)
|
||||||
|
|
||||||
|
// stop using any buffers before they get freed
|
||||||
|
if (pio->ssl) {
|
||||||
|
BSSLConnection_ReleaseBuffers(&pio->sslcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset decoder
|
||||||
|
PacketProtoDecoder_Reset(&pio->input_decoder);
|
||||||
|
|
||||||
|
// free sending
|
||||||
|
PacketPassConnector_DisconnectOutput(&pio->output_connector);
|
||||||
|
PacketStreamSender_Free(&pio->output_pss);
|
||||||
|
|
||||||
|
// free receiving
|
||||||
|
StreamRecvConnector_DisconnectInput(&pio->input_connector);
|
||||||
|
|
||||||
|
if (pio->ssl) {
|
||||||
|
// free BSSLConnection
|
||||||
|
BSSLConnection_Free(&pio->sslcon);
|
||||||
|
} else {
|
||||||
|
// free connection interfaces
|
||||||
|
BConnection_RecvAsync_Free(&pio->sock->con);
|
||||||
|
BConnection_SendAsync_Free(&pio->sock->con);
|
||||||
|
}
|
||||||
|
|
||||||
|
pio->sock = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sslcon_handler (StreamPeerIO *pio, int event)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&pio->d_obj);
|
||||||
|
ASSERT(pio->ssl)
|
||||||
|
ASSERT(pio->mode == MODE_CONNECT || pio->mode == MODE_LISTEN)
|
||||||
|
ASSERT(!(pio->mode == MODE_CONNECT) || pio->connect.state == CONNECT_STATE_FINISHED)
|
||||||
|
ASSERT(!(pio->mode == MODE_LISTEN) || pio->listen.state == LISTEN_STATE_FINISHED)
|
||||||
|
ASSERT(event == BSSLCONNECTION_EVENT_ERROR)
|
||||||
|
|
||||||
|
PeerLog(pio, BLOG_NOTICE, "SSL error");
|
||||||
|
|
||||||
|
reset_and_report_error(pio);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECStatus client_auth_certificate_callback (StreamPeerIO *pio, PRFileDesc *fd, PRBool checkSig, PRBool isServer)
|
||||||
|
{
|
||||||
|
ASSERT(pio->ssl)
|
||||||
|
ASSERT(pio->mode == MODE_CONNECT)
|
||||||
|
ASSERT(pio->connect.state == CONNECT_STATE_HANDSHAKE)
|
||||||
|
DebugObject_Access(&pio->d_obj);
|
||||||
|
|
||||||
|
// This callback is used to bypass checking the server's domain name, as peers
|
||||||
|
// don't have domain names. We byte-compare the certificate to the one reported
|
||||||
|
// by the server anyway.
|
||||||
|
|
||||||
|
SECStatus ret = SECFailure;
|
||||||
|
|
||||||
|
CERTCertificate *server_cert = SSL_PeerCertificate(pio->connect.sock.ssl_prfd);
|
||||||
|
if (!server_cert) {
|
||||||
|
PeerLog(pio, BLOG_ERROR, "SSL_PeerCertificate failed");
|
||||||
|
PORT_SetError(SSL_ERROR_BAD_CERTIFICATE);
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CERT_VerifyCertNow(CERT_GetDefaultCertDB(), server_cert, PR_TRUE, certUsageSSLServer, SSL_RevealPinArg(pio->connect.sock.ssl_prfd)) != SECSuccess) {
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare to certificate provided by the server
|
||||||
|
if (!compare_certificate(pio, server_cert)) {
|
||||||
|
PORT_SetError(SSL_ERROR_BAD_CERTIFICATE);
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = SECSuccess;
|
||||||
|
|
||||||
|
fail2:
|
||||||
|
CERT_DestroyCertificate(server_cert);
|
||||||
|
fail1:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECStatus client_client_auth_data_callback (StreamPeerIO *pio, PRFileDesc *fd, CERTDistNames *caNames, CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey)
|
||||||
|
{
|
||||||
|
ASSERT(pio->ssl)
|
||||||
|
ASSERT(pio->mode == MODE_CONNECT)
|
||||||
|
ASSERT(pio->connect.state == CONNECT_STATE_HANDSHAKE)
|
||||||
|
DebugObject_Access(&pio->d_obj);
|
||||||
|
|
||||||
|
CERTCertificate *cert = CERT_DupCertificate(pio->connect.ssl_cert);
|
||||||
|
if (!cert) {
|
||||||
|
PeerLog(pio, BLOG_ERROR, "CERT_DupCertificate failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECKEYPrivateKey *key = SECKEY_CopyPrivateKey(pio->connect.ssl_key);
|
||||||
|
if (!key) {
|
||||||
|
PeerLog(pio, BLOG_ERROR, "SECKEY_CopyPrivateKey failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pRetCert = cert;
|
||||||
|
*pRetKey = key;
|
||||||
|
return SECSuccess;
|
||||||
|
|
||||||
|
fail1:
|
||||||
|
CERT_DestroyCertificate(cert);
|
||||||
|
fail0:
|
||||||
|
return SECFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
int compare_certificate (StreamPeerIO *pio, CERTCertificate *cert)
|
||||||
|
{
|
||||||
|
ASSERT(pio->ssl)
|
||||||
|
|
||||||
|
SECItem der = cert->derCert;
|
||||||
|
if (der.len != pio->ssl_peer_cert_len || memcmp(der.data, pio->ssl_peer_cert, der.len)) {
|
||||||
|
PeerLog(pio, BLOG_NOTICE, "Client certificate doesn't match");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_state (StreamPeerIO *pio)
|
||||||
|
{
|
||||||
|
// free resources
|
||||||
|
switch (pio->mode) {
|
||||||
|
case MODE_NONE:
|
||||||
|
break;
|
||||||
|
case MODE_LISTEN:
|
||||||
|
switch (pio->listen.state) {
|
||||||
|
case LISTEN_STATE_FINISHED:
|
||||||
|
free_io(pio);
|
||||||
|
case LISTEN_STATE_GOTCLIENT:
|
||||||
|
if (pio->ssl) {
|
||||||
|
ASSERT_FORCE(PR_Close(pio->listen.sock->ssl_prfd) == PR_SUCCESS)
|
||||||
|
BConnection_RecvAsync_Free(&pio->listen.sock->con);
|
||||||
|
BConnection_SendAsync_Free(&pio->listen.sock->con);
|
||||||
|
}
|
||||||
|
BConnection_Free(&pio->listen.sock->con);
|
||||||
|
free(pio->listen.sock);
|
||||||
|
case LISTEN_STATE_LISTENER:
|
||||||
|
if (pio->listen.state == LISTEN_STATE_LISTENER) {
|
||||||
|
PasswordListener_RemoveEntry(pio->listen.listener, &pio->listen.pwentry);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ASSERT(0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MODE_CONNECT:
|
||||||
|
switch (pio->connect.state) {
|
||||||
|
case CONNECT_STATE_FINISHED:
|
||||||
|
free_io(pio);
|
||||||
|
case CONNECT_STATE_SENT:
|
||||||
|
case CONNECT_STATE_SENDING:
|
||||||
|
if (pio->connect.state == CONNECT_STATE_SENDING) {
|
||||||
|
if (pio->ssl) {
|
||||||
|
BSSLConnection_ReleaseBuffers(&pio->connect.sslcon);
|
||||||
|
}
|
||||||
|
SingleStreamSender_Free(&pio->connect.pwsender);
|
||||||
|
if (!pio->ssl) {
|
||||||
|
BConnection_SendAsync_Free(&pio->connect.sock.con);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case CONNECT_STATE_HANDSHAKE:
|
||||||
|
if (pio->ssl) {
|
||||||
|
if (pio->connect.state == CONNECT_STATE_HANDSHAKE || pio->connect.state == CONNECT_STATE_SENDING) {
|
||||||
|
BSSLConnection_Free(&pio->connect.sslcon);
|
||||||
|
}
|
||||||
|
ASSERT_FORCE(PR_Close(pio->connect.sock.ssl_prfd) == PR_SUCCESS)
|
||||||
|
BConnection_RecvAsync_Free(&pio->connect.sock.con);
|
||||||
|
BConnection_SendAsync_Free(&pio->connect.sock.con);
|
||||||
|
}
|
||||||
|
BConnection_Free(&pio->connect.sock.con);
|
||||||
|
case CONNECT_STATE_CONNECTING:
|
||||||
|
BConnector_Free(&pio->connect.connector);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ASSERT(0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ASSERT(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set mode none
|
||||||
|
pio->mode = MODE_NONE;
|
||||||
|
|
||||||
|
ASSERT(!pio->sock)
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_and_report_error (StreamPeerIO *pio)
|
||||||
|
{
|
||||||
|
reset_state(pio);
|
||||||
|
|
||||||
|
pio->handler_error(pio->user);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StreamPeerIO_Init (
|
||||||
|
StreamPeerIO *pio,
|
||||||
|
BReactor *reactor,
|
||||||
|
BThreadWorkDispatcher *twd,
|
||||||
|
int ssl,
|
||||||
|
int ssl_flags,
|
||||||
|
uint8_t *ssl_peer_cert,
|
||||||
|
int ssl_peer_cert_len,
|
||||||
|
int payload_mtu,
|
||||||
|
int sock_sndbuf,
|
||||||
|
PacketPassInterface *user_recv_if,
|
||||||
|
BLog_logfunc logfunc,
|
||||||
|
StreamPeerIO_handler_error handler_error,
|
||||||
|
void *user
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ASSERT(ssl == 0 || ssl == 1)
|
||||||
|
ASSERT(payload_mtu >= 0)
|
||||||
|
ASSERT(PacketPassInterface_GetMTU(user_recv_if) >= payload_mtu)
|
||||||
|
ASSERT(handler_error)
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
pio->reactor = reactor;
|
||||||
|
pio->twd = twd;
|
||||||
|
pio->ssl = ssl;
|
||||||
|
if (pio->ssl) {
|
||||||
|
pio->ssl_flags = ssl_flags;
|
||||||
|
pio->ssl_peer_cert = ssl_peer_cert;
|
||||||
|
pio->ssl_peer_cert_len = ssl_peer_cert_len;
|
||||||
|
}
|
||||||
|
pio->payload_mtu = payload_mtu;
|
||||||
|
pio->sock_sndbuf = sock_sndbuf;
|
||||||
|
pio->logfunc = logfunc;
|
||||||
|
pio->handler_error = handler_error;
|
||||||
|
pio->user = user;
|
||||||
|
|
||||||
|
// check payload MTU
|
||||||
|
if (pio->payload_mtu > PACKETPROTO_MAXPAYLOAD) {
|
||||||
|
PeerLog(pio, BLOG_ERROR, "payload MTU is too large");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init receiveing objects
|
||||||
|
StreamRecvConnector_Init(&pio->input_connector, BReactor_PendingGroup(pio->reactor));
|
||||||
|
if (!PacketProtoDecoder_Init(&pio->input_decoder, StreamRecvConnector_GetOutput(&pio->input_connector), user_recv_if, BReactor_PendingGroup(pio->reactor), pio,
|
||||||
|
(PacketProtoDecoder_handler_error)decoder_handler_error
|
||||||
|
)) {
|
||||||
|
PeerLog(pio, BLOG_ERROR, "FlowErrorDomain_Init failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init sending objects
|
||||||
|
PacketCopier_Init(&pio->output_user_copier, pio->payload_mtu, BReactor_PendingGroup(pio->reactor));
|
||||||
|
PacketProtoEncoder_Init(&pio->output_user_ppe, PacketCopier_GetOutput(&pio->output_user_copier), BReactor_PendingGroup(pio->reactor));
|
||||||
|
PacketPassConnector_Init(&pio->output_connector, PACKETPROTO_ENCLEN(pio->payload_mtu), BReactor_PendingGroup(pio->reactor));
|
||||||
|
if (!SinglePacketBuffer_Init(&pio->output_user_spb, PacketProtoEncoder_GetOutput(&pio->output_user_ppe), PacketPassConnector_GetInput(&pio->output_connector), BReactor_PendingGroup(pio->reactor))) {
|
||||||
|
PeerLog(pio, BLOG_ERROR, "SinglePacketBuffer_Init failed");
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set mode none
|
||||||
|
pio->mode = MODE_NONE;
|
||||||
|
|
||||||
|
// set no socket
|
||||||
|
pio->sock = NULL;
|
||||||
|
|
||||||
|
DebugObject_Init(&pio->d_obj);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail2:
|
||||||
|
PacketPassConnector_Free(&pio->output_connector);
|
||||||
|
PacketProtoEncoder_Free(&pio->output_user_ppe);
|
||||||
|
PacketCopier_Free(&pio->output_user_copier);
|
||||||
|
PacketProtoDecoder_Free(&pio->input_decoder);
|
||||||
|
fail1:
|
||||||
|
StreamRecvConnector_Free(&pio->input_connector);
|
||||||
|
fail0:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamPeerIO_Free (StreamPeerIO *pio)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&pio->d_obj);
|
||||||
|
|
||||||
|
// reset state
|
||||||
|
reset_state(pio);
|
||||||
|
|
||||||
|
// free sending objects
|
||||||
|
SinglePacketBuffer_Free(&pio->output_user_spb);
|
||||||
|
PacketPassConnector_Free(&pio->output_connector);
|
||||||
|
PacketProtoEncoder_Free(&pio->output_user_ppe);
|
||||||
|
PacketCopier_Free(&pio->output_user_copier);
|
||||||
|
|
||||||
|
// free receiveing objects
|
||||||
|
PacketProtoDecoder_Free(&pio->input_decoder);
|
||||||
|
StreamRecvConnector_Free(&pio->input_connector);
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketPassInterface * StreamPeerIO_GetSendInput (StreamPeerIO *pio)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&pio->d_obj);
|
||||||
|
|
||||||
|
return PacketCopier_GetInput(&pio->output_user_copier);
|
||||||
|
}
|
||||||
|
|
||||||
|
int StreamPeerIO_Connect (StreamPeerIO *pio, BAddr addr, uint64_t password, CERTCertificate *ssl_cert, SECKEYPrivateKey *ssl_key)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&pio->d_obj);
|
||||||
|
|
||||||
|
// reset state
|
||||||
|
reset_state(pio);
|
||||||
|
|
||||||
|
// check address
|
||||||
|
if (!BConnection_AddressSupported(addr)) {
|
||||||
|
PeerLog(pio, BLOG_ERROR, "BConnection_AddressSupported failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init connector
|
||||||
|
if (!BConnector_Init(&pio->connect.connector, addr, pio->reactor, pio, (BConnector_handler)connector_handler)) {
|
||||||
|
PeerLog(pio, BLOG_ERROR, "BConnector_Init failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remember data
|
||||||
|
if (pio->ssl) {
|
||||||
|
pio->connect.ssl_cert = ssl_cert;
|
||||||
|
pio->connect.ssl_key = ssl_key;
|
||||||
|
}
|
||||||
|
pio->connect.password = htol64(password);
|
||||||
|
|
||||||
|
// set state
|
||||||
|
pio->mode = MODE_CONNECT;
|
||||||
|
pio->connect.state = CONNECT_STATE_CONNECTING;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail0:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamPeerIO_Listen (StreamPeerIO *pio, PasswordListener *listener, uint64_t *password)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&pio->d_obj);
|
||||||
|
ASSERT(listener->ssl == pio->ssl)
|
||||||
|
|
||||||
|
// reset state
|
||||||
|
reset_state(pio);
|
||||||
|
|
||||||
|
// add PasswordListener entry
|
||||||
|
uint64_t newpass = PasswordListener_AddEntry(listener, &pio->listen.pwentry, (PasswordListener_handler_client)listener_handler_client, pio);
|
||||||
|
|
||||||
|
// remember data
|
||||||
|
pio->listen.listener = listener;
|
||||||
|
|
||||||
|
// set state
|
||||||
|
pio->mode = MODE_LISTEN;
|
||||||
|
pio->listen.state = LISTEN_STATE_LISTENER;
|
||||||
|
|
||||||
|
*password = newpass;
|
||||||
|
}
|
|
@ -0,0 +1,222 @@
|
||||||
|
/**
|
||||||
|
* @file StreamPeerIO.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @section DESCRIPTION
|
||||||
|
*
|
||||||
|
* Object used for communicating with a peer over TCP.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_CLIENT_STREAMPEERIO_H
|
||||||
|
#define BADVPN_CLIENT_STREAMPEERIO_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <cert.h>
|
||||||
|
#include <keyhi.h>
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <base/BLog.h>
|
||||||
|
#include <system/BReactor.h>
|
||||||
|
#include <system/BConnection.h>
|
||||||
|
#include <structure/LinkedList1.h>
|
||||||
|
#include <flow/PacketProtoDecoder.h>
|
||||||
|
#include <flow/PacketStreamSender.h>
|
||||||
|
#include <flow/SinglePacketBuffer.h>
|
||||||
|
#include <flow/PacketProtoEncoder.h>
|
||||||
|
#include <flow/PacketCopier.h>
|
||||||
|
#include <flow/PacketPassConnector.h>
|
||||||
|
#include <flow/StreamRecvConnector.h>
|
||||||
|
#include <flow/SingleStreamSender.h>
|
||||||
|
#include <client/PasswordListener.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function invoked when an error occurs with the peer connection.
|
||||||
|
* The object has entered default state.
|
||||||
|
* May be called from within a sending Send call.
|
||||||
|
*
|
||||||
|
* @param user value given to {@link StreamPeerIO_Init}.
|
||||||
|
*/
|
||||||
|
typedef void (*StreamPeerIO_handler_error) (void *user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object used for communicating with a peer over TCP.
|
||||||
|
* The object has a logical state which can be one of the following:
|
||||||
|
* - default state
|
||||||
|
* - listening state
|
||||||
|
* - connecting state
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
// common arguments
|
||||||
|
BReactor *reactor;
|
||||||
|
BThreadWorkDispatcher *twd;
|
||||||
|
int ssl;
|
||||||
|
int ssl_flags;
|
||||||
|
uint8_t *ssl_peer_cert;
|
||||||
|
int ssl_peer_cert_len;
|
||||||
|
int payload_mtu;
|
||||||
|
int sock_sndbuf;
|
||||||
|
BLog_logfunc logfunc;
|
||||||
|
StreamPeerIO_handler_error handler_error;
|
||||||
|
void *user;
|
||||||
|
|
||||||
|
// persistent I/O modules
|
||||||
|
|
||||||
|
// base sending objects
|
||||||
|
PacketCopier output_user_copier;
|
||||||
|
PacketProtoEncoder output_user_ppe;
|
||||||
|
SinglePacketBuffer output_user_spb;
|
||||||
|
PacketPassConnector output_connector;
|
||||||
|
|
||||||
|
// receiving objects
|
||||||
|
StreamRecvConnector input_connector;
|
||||||
|
PacketProtoDecoder input_decoder;
|
||||||
|
|
||||||
|
// connection side
|
||||||
|
int mode;
|
||||||
|
|
||||||
|
union {
|
||||||
|
// listening data
|
||||||
|
struct {
|
||||||
|
int state;
|
||||||
|
PasswordListener *listener;
|
||||||
|
PasswordListener_pwentry pwentry;
|
||||||
|
sslsocket *sock;
|
||||||
|
} listen;
|
||||||
|
// connecting data
|
||||||
|
struct {
|
||||||
|
int state;
|
||||||
|
CERTCertificate *ssl_cert;
|
||||||
|
SECKEYPrivateKey *ssl_key;
|
||||||
|
BConnector connector;
|
||||||
|
sslsocket sock;
|
||||||
|
BSSLConnection sslcon;
|
||||||
|
uint64_t password;
|
||||||
|
SingleStreamSender pwsender;
|
||||||
|
} connect;
|
||||||
|
};
|
||||||
|
|
||||||
|
// socket data
|
||||||
|
sslsocket *sock;
|
||||||
|
BSSLConnection sslcon;
|
||||||
|
|
||||||
|
// sending objects
|
||||||
|
PacketStreamSender output_pss;
|
||||||
|
|
||||||
|
DebugObject d_obj;
|
||||||
|
} StreamPeerIO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the object.
|
||||||
|
* The object is initialized in default state.
|
||||||
|
* {@link BLog_Init} must have been done.
|
||||||
|
* {@link BNetwork_GlobalInit} must have been done.
|
||||||
|
* {@link BSSLConnection_GlobalInit} must have been done if using SSL.
|
||||||
|
*
|
||||||
|
* @param pio the object
|
||||||
|
* @param reactor reactor we live in
|
||||||
|
* @param twd thread work dispatcher. May be NULL if ssl_flags does not request performing SSL
|
||||||
|
* operations in threads.
|
||||||
|
* @param ssl if nonzero, SSL will be used for peer connection
|
||||||
|
* @param ssl_flags flags passed down to {@link BSSLConnection_MakeBackend}. May be used to
|
||||||
|
* request performing SSL operations in threads.
|
||||||
|
* @param ssl_peer_cert if using SSL, the certificate we expect the peer to have
|
||||||
|
* @param ssl_peer_cert_len if using SSL, the length of the certificate
|
||||||
|
* @param payload_mtu maximum packet size as seen from the user. Must be >=0.
|
||||||
|
* @param sock_sndbuf socket SO_SNDBUF option. Specify <=0 to not set it.
|
||||||
|
* @param user_recv_if interface to use for submitting received packets. Its MTU
|
||||||
|
* must be >=payload_mtu.
|
||||||
|
* @param logfunc function which prepends the log prefix using {@link BLog_Append}
|
||||||
|
* @param handler_error handler function invoked when a connection error occurs
|
||||||
|
* @param user value to pass to handler functions
|
||||||
|
* @return 1 on success, 0 on failure
|
||||||
|
*/
|
||||||
|
int StreamPeerIO_Init (
|
||||||
|
StreamPeerIO *pio,
|
||||||
|
BReactor *reactor,
|
||||||
|
BThreadWorkDispatcher *twd,
|
||||||
|
int ssl,
|
||||||
|
int ssl_flags,
|
||||||
|
uint8_t *ssl_peer_cert,
|
||||||
|
int ssl_peer_cert_len,
|
||||||
|
int payload_mtu,
|
||||||
|
int sock_sndbuf,
|
||||||
|
PacketPassInterface *user_recv_if,
|
||||||
|
BLog_logfunc logfunc,
|
||||||
|
StreamPeerIO_handler_error handler_error,
|
||||||
|
void *user
|
||||||
|
) WARN_UNUSED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the object.
|
||||||
|
*
|
||||||
|
* @param pio the object
|
||||||
|
*/
|
||||||
|
void StreamPeerIO_Free (StreamPeerIO *pio);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the interface for sending packets to the peer.
|
||||||
|
* The OTP warning handler may be called from within Send calls
|
||||||
|
* to the interface.
|
||||||
|
*
|
||||||
|
* @param pio the object
|
||||||
|
* @return interface for sending packets to the peer
|
||||||
|
*/
|
||||||
|
PacketPassInterface * StreamPeerIO_GetSendInput (StreamPeerIO *pio);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts an attempt to connect to the peer.
|
||||||
|
* On success, the object enters connecting state.
|
||||||
|
* On failure, the object enters default state.
|
||||||
|
*
|
||||||
|
* @param pio the object
|
||||||
|
* @param addr address to connect to
|
||||||
|
* @param password identification code to send to the peer
|
||||||
|
* @param ssl_cert if using SSL, the client certificate to use. This object does not
|
||||||
|
* take ownership of the certificate; it must remain valid until
|
||||||
|
* the object is reset.
|
||||||
|
* @param ssl_key if using SSL, the private key to use. This object does not take
|
||||||
|
* ownership of the key; it must remain valid until the object is reset.
|
||||||
|
* @return 1 on success, 0 on failure
|
||||||
|
*/
|
||||||
|
int StreamPeerIO_Connect (StreamPeerIO *pio, BAddr addr, uint64_t password, CERTCertificate *ssl_cert, SECKEYPrivateKey *ssl_key) WARN_UNUSED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts an attempt to accept a connection from the peer.
|
||||||
|
* The object enters listening state.
|
||||||
|
*
|
||||||
|
* @param pio the object
|
||||||
|
* @param listener {@link PasswordListener} object to use for accepting a connection.
|
||||||
|
* The listener must have SSL enabled if and only if this object has
|
||||||
|
* SSL enabled. The listener must be available until the object is
|
||||||
|
* reset or {@link StreamPeerIO_handler_up} is called.
|
||||||
|
* @param password will return the identification code the peer should send when connecting
|
||||||
|
*/
|
||||||
|
void StreamPeerIO_Listen (StreamPeerIO *pio, PasswordListener *listener, uint64_t *password);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,316 @@
|
||||||
|
.TH badvpn-client 8 "14 July 2011"
|
||||||
|
.SH NAME
|
||||||
|
badvpn-client \- VPN node daemon for the BadVPN peer-to-peer VPN system
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B badvpn-client
|
||||||
|
.RS
|
||||||
|
.RB "[" --help "]"
|
||||||
|
.br
|
||||||
|
.RB "[" --version "]"
|
||||||
|
.br
|
||||||
|
.RB "[" --logger " <stdout/syslog>]"
|
||||||
|
.br
|
||||||
|
(logger=syslog?
|
||||||
|
.br
|
||||||
|
.RS
|
||||||
|
.br
|
||||||
|
.RB "[" --syslog-facility " <string>]"
|
||||||
|
.br
|
||||||
|
.RB "[" --syslog-ident " <string>]"
|
||||||
|
.br
|
||||||
|
.RE
|
||||||
|
)
|
||||||
|
.br
|
||||||
|
.RB "[" --loglevel " <0-5/none/error/warning/notice/info/debug>]"
|
||||||
|
.br
|
||||||
|
.RB "[" --channel-loglevel " <channel-name> <0-5/none/error/warning/notice/info/debug>] ..."
|
||||||
|
.br
|
||||||
|
.RB "[" --threads " <integer>]"
|
||||||
|
.br
|
||||||
|
.RB "[" --ssl " " --nssdb " <string> " --client-cert-name " <string>]"
|
||||||
|
.br
|
||||||
|
.RB "[" --server-name " <string>]"
|
||||||
|
.br
|
||||||
|
.BR --server-addr " <addr>"
|
||||||
|
.br
|
||||||
|
.RB "[" --tapdev " <name>]"
|
||||||
|
.br
|
||||||
|
.RB "[" --scope " <scope_name>] ..."
|
||||||
|
.br
|
||||||
|
[
|
||||||
|
.br
|
||||||
|
.RS
|
||||||
|
.BR --bind-addr " <addr>"
|
||||||
|
.br
|
||||||
|
.RB "(transport-mode=udp? " --num-ports " <num>)"
|
||||||
|
.br
|
||||||
|
.RB "[" --ext-addr " <addr / {server_reported}:port> <scope_name>] ..."
|
||||||
|
.br
|
||||||
|
.RE
|
||||||
|
] ...
|
||||||
|
.br
|
||||||
|
.BR --transport-mode " <udp/tcp>"
|
||||||
|
.br
|
||||||
|
(transport-mode=udp?
|
||||||
|
.br
|
||||||
|
.RS
|
||||||
|
.BR --encryption-mode " <blowfish/aes/none>"
|
||||||
|
.br
|
||||||
|
.BR --hash-mode " <md5/sha1/none>"
|
||||||
|
.br
|
||||||
|
.RB "[" --otp " <blowfish/aes> <num> <num-warn>]"
|
||||||
|
.br
|
||||||
|
.RB "[" --fragmentation-latency " <milliseconds>]"
|
||||||
|
.br
|
||||||
|
.RE
|
||||||
|
)
|
||||||
|
.br
|
||||||
|
(transport-mode=tcp?
|
||||||
|
.br
|
||||||
|
.RS
|
||||||
|
.RB "(ssl? [" --peer-ssl "])"
|
||||||
|
.br
|
||||||
|
.RB "[" --peer-tcp-socket-sndbuf " <bytes / 0>]"
|
||||||
|
.br
|
||||||
|
.RE
|
||||||
|
)
|
||||||
|
.br
|
||||||
|
.RB "[" --send-buffer-size " <num-packets>]"
|
||||||
|
.br
|
||||||
|
.RB "[" --send-buffer-relay-size " <num-packets>]"
|
||||||
|
.br
|
||||||
|
.RB "[" --max-macs " <num>]"
|
||||||
|
.br
|
||||||
|
.RB "[" --max-groups " <num>]"
|
||||||
|
.br
|
||||||
|
.RB "[" --igmp-group-membership-interval " <ms>]"
|
||||||
|
.br
|
||||||
|
.RB "[" --igmp-last-member-query-time " <ms>]"
|
||||||
|
.br
|
||||||
|
.RB "[" --allow-peer-talk-without-ssl "]"
|
||||||
|
.br
|
||||||
|
.RE
|
||||||
|
.SH INTRODUCTION
|
||||||
|
.P
|
||||||
|
This page documents the BadVPN client, a daemon for a node in a BadVPN VPN network.
|
||||||
|
For a general description of BadVPN, see
|
||||||
|
.BR badvpn (7).
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.P
|
||||||
|
The BadVPN client is a daemon that runs on a VPN node. It opens the TAP device, connects to
|
||||||
|
the server, then keeps running while attempting to establish data connection to peers and
|
||||||
|
tranferring data between the TAP device and the peers. Once it initializes, the program only
|
||||||
|
terminates if it loses connection to the server, or if a signal is received.
|
||||||
|
.SH OPTIONS
|
||||||
|
.P
|
||||||
|
The BadVPN client is configured entirely from command line.
|
||||||
|
.TP
|
||||||
|
.BR --help
|
||||||
|
Print version and command line syntax and exit.
|
||||||
|
.TP
|
||||||
|
.BR --version
|
||||||
|
Print version and exit.
|
||||||
|
.TP
|
||||||
|
.BR --logger " <stdout/syslog>"
|
||||||
|
Select where to log messages. Default is stdout. Syslog is not available on Windows.
|
||||||
|
.TP
|
||||||
|
.BR --syslog-facility " <string>"
|
||||||
|
When logging to syslog, set the logging facility. The facility name must be in lower case.
|
||||||
|
.TP
|
||||||
|
.BR --syslog-ident " <string>"
|
||||||
|
When logging to syslog, set the ident.
|
||||||
|
.TP
|
||||||
|
.BR --loglevel " <0-5/none/error/warning/notice/info/debug>"
|
||||||
|
Set the default logging level.
|
||||||
|
.TP
|
||||||
|
.BR --channel-loglevel " <channel-name> <0-5/none/error/warning/notice/info/debug>"
|
||||||
|
Set the logging level for a specific logging channel.
|
||||||
|
.TP
|
||||||
|
.BR --threads " <integer>"
|
||||||
|
Hint for the number of additional threads to use for potentionally long computations (such as
|
||||||
|
encryption and OTP generation). If zero (0) (default), additional threads will be disabled and all
|
||||||
|
computations will be done in the event loop. If negative (<0), a guess will be made, possibly
|
||||||
|
based on the number of CPUs. If positive (>0), the given number of threads will be used.
|
||||||
|
.TP
|
||||||
|
.BR --ssl
|
||||||
|
Use TLS. Requires --nssdb and --server-cert-name.
|
||||||
|
.TP
|
||||||
|
.BR --nssdb " <string>"
|
||||||
|
When using TLS, the NSS database to use. Probably something like sql:/some/folder.
|
||||||
|
.TP
|
||||||
|
.BR --client-cert-name " <string>"
|
||||||
|
When using TLS, the name of the certificate to use. The certificate must be readily accessible.
|
||||||
|
.TP
|
||||||
|
.BR --server-name " <string>"
|
||||||
|
Set the name of the server used for validating the server's certificate. The server name defaults
|
||||||
|
to the the name in the server address (or a numeric address).
|
||||||
|
.TP
|
||||||
|
.BR --server-addr " <addr>"
|
||||||
|
Set the address for the server to listen on. See below for address format.
|
||||||
|
.TP
|
||||||
|
.BR --tapdev " <name>"
|
||||||
|
Set the TAP device to use. See below on how to configure the device. A TAP device is a virtual card
|
||||||
|
in the operating system, but rather than receiving from and sending frames to a piece of hardware,
|
||||||
|
a program (this one) opens it to read from and write frames into. If the VPN network is set up correctly,
|
||||||
|
the TAP devices on the VPN nodes will act as if they were all connected into a network switch.
|
||||||
|
.TP
|
||||||
|
.BR --scope " <scope_name>"
|
||||||
|
Add an address scope allowed for connecting to peers. May be specified multiple times to add multiple
|
||||||
|
scopes. The order of the scopes is irrelevant. Note that it must actually be possible to connect
|
||||||
|
to addresses in the given scope; when another peer binds for us to connect to, we choose the first
|
||||||
|
external address whose scope we recognize, and do not attempt further external addresses, even if
|
||||||
|
establishing the connection fails.
|
||||||
|
.TP
|
||||||
|
.BR --bind-addr " <addr>"
|
||||||
|
Add an address to allow binding on. See below for address format. When attempting to bind in order
|
||||||
|
for some peer to connect to us, the addresses will be tried in the order they are specified. If UDP
|
||||||
|
data transport is being used, a --num-ports option must follow to specify how many continuous ports
|
||||||
|
to allow binding to. For the address to be useful, one or more --ext-addr options must follow.
|
||||||
|
Note that when two peers need to establish a data connection, it is arbitrary which one will attempt
|
||||||
|
to bind first.
|
||||||
|
.TP
|
||||||
|
.BR --num-ports " <num>"
|
||||||
|
When using UDP transport, set the number of continuous ports for a previously specified bind address.
|
||||||
|
Must follow a previous --bind-addr option.
|
||||||
|
.TP
|
||||||
|
.BR --ext-addr " <addr / {server_reported}:port> <scope_name>"
|
||||||
|
Add an external address for a previously specified bind address. Must follow a previous --bind-addr
|
||||||
|
option. May be specified multiple times to add multiple external addresses. See below for address
|
||||||
|
format. Additionally, the IP address part can be {server_reported} to use the IPv4 address as the
|
||||||
|
server sees us. The external addresses are tried by the connecting peer in the order they are specified.
|
||||||
|
Note that the connecting peer only attempts to connect to the first address whose scope it recognizes
|
||||||
|
and does not try other addresses. This means that all addresses must work for be able to communicate.
|
||||||
|
.TP
|
||||||
|
.BR --transport-mode " <udp/tcp>"
|
||||||
|
Sets the transport protocol for data connections. UDP is recommended and works best for most networks.
|
||||||
|
TCP can be used instead if the underlying network has high packet loss which your virtual network
|
||||||
|
cannot tolerate. Must match on all peers.
|
||||||
|
.TP
|
||||||
|
.BR --encryption-mode " <blowfish/aes/none>"
|
||||||
|
When using UDP transport, sets the encryption mode. None means no encryption, other options mean
|
||||||
|
a specific cipher. Note that encryption is only useful if clients use TLS to connect to the server.
|
||||||
|
The encryption mode must match on all peers.
|
||||||
|
.TP
|
||||||
|
.BR --hash-mode " <md5/sha1/none>"
|
||||||
|
When using UDP transport, sets the hashing mode. None means no hashes, other options mean a specific
|
||||||
|
type of hash. Note that hashing is only useful if encryption is used as well. The hash mode must
|
||||||
|
match on all peers.
|
||||||
|
.TP
|
||||||
|
.BR --otp " <blowfish/aes> <num> <num-warn>"
|
||||||
|
When using UDP transport, enables one-time passwords. The first argument specifies a block cipher
|
||||||
|
used to generate passwords from a seed. The second argument specifies how many passwords are
|
||||||
|
generated from a single seed. The third argument specifies after how many passwords used up for
|
||||||
|
sending packets an attempt is made to negotiate a new seed with the other peer. num must be >0,
|
||||||
|
and num-warn must be >0 and <=num. The difference (num - num-warn) should be large enough to allow
|
||||||
|
a new seed to be negotiated before the sender runs out of passwords. Negotiating a seed involves
|
||||||
|
the sending peer sending it to the receiving peer via the server and the receiving peer confirming
|
||||||
|
it via the server. Note that one-time passwords are only useful if clients use TLS to connect to the
|
||||||
|
server. The OTP option must match on all peers, except for num-warn.
|
||||||
|
.TP
|
||||||
|
.BR --fragmentation-latency " <milliseconds>"
|
||||||
|
When using UDP transport, sets the maximum latency to sacrifice in order to pack frames into data
|
||||||
|
packets more efficiently. If it is >=0, a timer of that many milliseconds is used to wait for further
|
||||||
|
frames to put into an incomplete packet since the first chunk of the packet was written. If it is
|
||||||
|
<0, packets are sent out immediately. Defaults to 0, which is the recommended setting.
|
||||||
|
.TP
|
||||||
|
.BR --peer-ssl
|
||||||
|
When using TCP transport, enables TLS for data connections. Requires using TLS for server connection.
|
||||||
|
For this to work, the peers must trust each others' cerificates, and the cerificates must grant the
|
||||||
|
TLS server usage context. This option must match on all peers.
|
||||||
|
.TP
|
||||||
|
.BR --peer-tcp-socket-sndbuf " <bytes / 0>"
|
||||||
|
Sets the value of the SO_SNDBUF socket option for peer TCP sockets (zero to not set). Lower values
|
||||||
|
will improve fairness when data from multiple sources (local and relaying) is being sent to a
|
||||||
|
given peer, but may result in lower bandwidth if the network's bandwidth-delay product is too big.
|
||||||
|
.TP
|
||||||
|
.BR --send-buffer-size " <num-packets>"
|
||||||
|
Sets the minimum size of the peers' send buffers for sending frames originating from this system, in
|
||||||
|
number of packets.
|
||||||
|
.TP
|
||||||
|
.BR --send-buffer-relay-size " <num-packets>"
|
||||||
|
Sets the minimum size of the peers' send buffers for relaying frames from other peers, in number of
|
||||||
|
packets.
|
||||||
|
.TP
|
||||||
|
.BR --max-macs " <num>"
|
||||||
|
Sets the maximum number of MAC addresses to remember for a peer. When the number is exceeded, the least
|
||||||
|
recently used slot will be reused.
|
||||||
|
.TP
|
||||||
|
.BR --max-groups " <num>"
|
||||||
|
Sets the maximum number of IGMP group memberships to remember for a peer. When the number is exceeded,
|
||||||
|
the least recently used slot will be reused.
|
||||||
|
.TP
|
||||||
|
.BR --igmp-group-membership-interval " <ms>"
|
||||||
|
Sets the Group Membership Interval parameter for IGMP snooping, in milliseconds.
|
||||||
|
.TP
|
||||||
|
.BR --igmp-last-member-query-time " <ms>"
|
||||||
|
Sets the Last Member Query Time parameter for IGMP snooping, in milliseconds.
|
||||||
|
.TP
|
||||||
|
.BR --allow-peer-talk-without-ssl
|
||||||
|
When SSL is enabled, the clients not only connect to the server using SSL, but also exchange messages through
|
||||||
|
the server through another layer of SSL. This protects the messages from attacks on the server. Older versions
|
||||||
|
of BadVPN (<1.999.109), however, do not support this. This option allows older and newer clients to
|
||||||
|
interoperate by not using SSL if the other peer does not support it. It does however negate the security
|
||||||
|
benefits of using SSL, since the (potentionally compromised) server can then order peers not to use SSL.
|
||||||
|
.SH "EXIT CODE"
|
||||||
|
.P
|
||||||
|
If initialization fails, exits with code 1. Otherwise runs until termination is requested or server connection
|
||||||
|
is broken and exits with code 1.
|
||||||
|
.SH "ADDRESS FORMAT"
|
||||||
|
.P
|
||||||
|
Addresses have the form ipaddr:port, where ipaddr is either an IPv4 address (name or numeric), or an
|
||||||
|
IPv6 address enclosed in brackets [] (name or numeric again).
|
||||||
|
.SH "TAP DEVICE CONFIGURATION"
|
||||||
|
.P
|
||||||
|
To use this program, you first have to configure a TAP network device that will act as an endpoint for
|
||||||
|
the virtual network. The configuration depends on your operating system.
|
||||||
|
.P
|
||||||
|
Note that the client program does not configure the TAP device in any way; it only reads and writes
|
||||||
|
frames from/to it. You are responsible for configuring it (e.g. putting it up and setting its IP address).
|
||||||
|
.P
|
||||||
|
.B Linux
|
||||||
|
.P
|
||||||
|
You need to enable the kernel configuration option CONFIG_TUN. If you enabled it as a module, you may
|
||||||
|
have to load it (`modprobe tun`) before you can create the device.
|
||||||
|
.P
|
||||||
|
Then you should create a persistent TAP device for the VPN client program to open. This can be done with
|
||||||
|
either the
|
||||||
|
.B tunctl
|
||||||
|
or the
|
||||||
|
.B openvpn
|
||||||
|
program. The device will be associated with a user account that will have permission to use it, which should
|
||||||
|
be the same user as the client program will run as (not root!). To create the device with tunctl, use `tunctl -u <user> -t tapN`,
|
||||||
|
and to create it with openvpn, use `openvpn --mktun --user <user> --dev tapN`, where N is a number that identifies the
|
||||||
|
TAP device.
|
||||||
|
.P
|
||||||
|
Once the TAP device is created, pass `--tapdev tapN` to the client program to make it use this device. Note that the
|
||||||
|
device will not be preserved across a shutdown of the system; consult your OS documentaton if you want to automate
|
||||||
|
the creation or configuration of the device.
|
||||||
|
.P
|
||||||
|
.B Windows
|
||||||
|
.P
|
||||||
|
Windows does not come with a TAP driver. The client program uses the TAP-Win32 driver, which is part of OpenVPN.
|
||||||
|
You need to install the OpenVPN open source (!) version, and in the installer enable at least the
|
||||||
|
`TAP Virtual Ethernet Adapter` and `Add Shortcuts to Start Menu` options.
|
||||||
|
You can get the installer at
|
||||||
|
.br
|
||||||
|
<http://openvpn.net/index.php/open-source/downloads.html>.
|
||||||
|
.P
|
||||||
|
The OpenVPN installer automatically creates one TAP device on your system when it's run for the first time.
|
||||||
|
To create another device, use `Programs -> OpenVPN -> Utilities -> Add a new TAP virtual ethernet adapter`.
|
||||||
|
You may have to install OpenVPN once again to make this shortcut appear.
|
||||||
|
.P
|
||||||
|
Once you have a TAP device, you can configure it like a physical network card. You can recognize TAP devices
|
||||||
|
by their `Device Name` field.
|
||||||
|
.P
|
||||||
|
To use the device, pass `--tapdev "<driver_name>:<interface_name>"` to the client program, where <driver_name> is the name of
|
||||||
|
the TAP driver (tap0901 for OpenVPN 2.1 and 2.2) (case sensitive), and <interface_name> is the (human) name of the TAP
|
||||||
|
network interface (e.g. `Local Area Connection 2`).
|
||||||
|
.SH "EXAMPLES"
|
||||||
|
.P
|
||||||
|
For examples of using BadVPN, see
|
||||||
|
.BR badvpn (7).
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.BR badvpn-server (8),
|
||||||
|
.BR badvpn (7)
|
||||||
|
.SH AUTHORS
|
||||||
|
Ambroz Bizjak <ambrop7@gmail.com>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,193 @@
|
||||||
|
/**
|
||||||
|
* @file client.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <protocol/scproto.h>
|
||||||
|
#include <structure/LinkedList1.h>
|
||||||
|
#include <flow/PacketPassFairQueue.h>
|
||||||
|
#include <flow/SinglePacketBuffer.h>
|
||||||
|
#include <flow/PacketRecvConnector.h>
|
||||||
|
#include <client/DatagramPeerIO.h>
|
||||||
|
#include <client/StreamPeerIO.h>
|
||||||
|
#include <client/DataProto.h>
|
||||||
|
#include <client/DPReceive.h>
|
||||||
|
#include <client/FrameDecider.h>
|
||||||
|
#include <client/PeerChat.h>
|
||||||
|
#include <client/SinglePacketSource.h>
|
||||||
|
|
||||||
|
// NOTE: all time values are in milliseconds
|
||||||
|
|
||||||
|
// name of the program
|
||||||
|
#define PROGRAM_NAME "client"
|
||||||
|
|
||||||
|
// server output buffer size
|
||||||
|
#define SERVER_BUFFER_MIN_PACKETS 200
|
||||||
|
|
||||||
|
// maximum UDP payload size
|
||||||
|
#define CLIENT_UDP_MTU 1472
|
||||||
|
|
||||||
|
// maximum number of pending TCP PasswordListener clients
|
||||||
|
#define TCP_MAX_PASSWORD_LISTENER_CLIENTS 50
|
||||||
|
|
||||||
|
// maximum number of peers
|
||||||
|
#define DEFAULT_MAX_PEERS 256
|
||||||
|
// maximum number of peer's MAC addresses to remember
|
||||||
|
#define PEER_DEFAULT_MAX_MACS 16
|
||||||
|
// maximum number of multicast addresses per peer
|
||||||
|
#define PEER_DEFAULT_MAX_GROUPS 16
|
||||||
|
// how long we wait for a packet to reach full size before sending it (see FragmentProtoDisassembler latency argument)
|
||||||
|
#define PEER_DEFAULT_UDP_FRAGMENTATION_LATENCY 0
|
||||||
|
// value related to how much out-of-order input we tolerate (see FragmentProtoAssembler num_frames argument)
|
||||||
|
#define PEER_UDP_ASSEMBLER_NUM_FRAMES 4
|
||||||
|
// socket send buffer (SO_SNDBUF) for peer TCP connections, <=0 to not set
|
||||||
|
#define PEER_DEFAULT_TCP_SOCKET_SNDBUF 1048576
|
||||||
|
// keep-alive packet interval for p2p communication
|
||||||
|
#define PEER_KEEPALIVE_INTERVAL 10000
|
||||||
|
// keep-alive receive timer for p2p communication (after how long to consider the link down)
|
||||||
|
#define PEER_KEEPALIVE_RECEIVE_TIMER 22000
|
||||||
|
// size of frame send buffer, in number of frames
|
||||||
|
#define PEER_DEFAULT_SEND_BUFFER_SIZE 32
|
||||||
|
// size of frame send buffer for relayed packets, in number of frames
|
||||||
|
#define PEER_DEFAULT_SEND_BUFFER_RELAY_SIZE 32
|
||||||
|
// time after an unused relay flow is freed (-1 for never)
|
||||||
|
#define PEER_RELAY_FLOW_INACTIVITY_TIME 10000
|
||||||
|
// retry time
|
||||||
|
#define PEER_RETRY_TIME 5000
|
||||||
|
|
||||||
|
// for how long a peer can send no Membership Reports for a group
|
||||||
|
// before the peer and group are disassociated
|
||||||
|
#define DEFAULT_IGMP_GROUP_MEMBERSHIP_INTERVAL 260000
|
||||||
|
// how long to wait for joins after a Group Specific query has been
|
||||||
|
// forwarded to a peer before assuming there are no listeners at the peer
|
||||||
|
#define DEFAULT_IGMP_LAST_MEMBER_QUERY_TIME 2000
|
||||||
|
|
||||||
|
// maximum bind addresses
|
||||||
|
#define MAX_BIND_ADDRS 8
|
||||||
|
// maximum external addresses per bind address
|
||||||
|
#define MAX_EXT_ADDRS 8
|
||||||
|
// maximum scopes
|
||||||
|
#define MAX_SCOPES 8
|
||||||
|
|
||||||
|
//#define SIMULATE_PEER_OUT_OF_BUFFER 70
|
||||||
|
|
||||||
|
struct server_flow {
|
||||||
|
PacketPassFairQueueFlow qflow;
|
||||||
|
SinglePacketBuffer encoder_buffer;
|
||||||
|
PacketRecvConnector connector;
|
||||||
|
int connected;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct peer_data {
|
||||||
|
// peer identifier
|
||||||
|
peerid_t id;
|
||||||
|
|
||||||
|
// flags provided by the server
|
||||||
|
int flags;
|
||||||
|
|
||||||
|
// certificate reported by the server, defined only if using SSL
|
||||||
|
uint8_t cert[SCID_NEWCLIENT_MAX_CERT_LEN];
|
||||||
|
int cert_len;
|
||||||
|
char *common_name;
|
||||||
|
|
||||||
|
// init job
|
||||||
|
BPending job_init;
|
||||||
|
|
||||||
|
// server flow
|
||||||
|
struct server_flow *server_flow;
|
||||||
|
|
||||||
|
// chat
|
||||||
|
int have_chat;
|
||||||
|
PeerChat chat;
|
||||||
|
int chat_send_msg_len;
|
||||||
|
|
||||||
|
// resetpeer source (when chat fails)
|
||||||
|
int have_resetpeer;
|
||||||
|
uint8_t resetpeer_packet[sizeof(struct packetproto_header) + sizeof(struct sc_header) + sizeof(struct sc_client_resetpeer)];
|
||||||
|
SinglePacketSource resetpeer_source;
|
||||||
|
|
||||||
|
// local flow
|
||||||
|
DataProtoFlow local_dpflow;
|
||||||
|
|
||||||
|
// frame decider peer
|
||||||
|
FrameDeciderPeer decider_peer;
|
||||||
|
|
||||||
|
// receive peer
|
||||||
|
DPReceivePeer receive_peer;
|
||||||
|
|
||||||
|
// flag if link objects are initialized
|
||||||
|
int have_link;
|
||||||
|
|
||||||
|
// receive receiver
|
||||||
|
DPReceiveReceiver receive_receiver;
|
||||||
|
|
||||||
|
// transport-specific link objects
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
DatagramPeerIO pio;
|
||||||
|
uint16_t sendseed_nextid;
|
||||||
|
int sendseed_sent;
|
||||||
|
uint16_t sendseed_sent_id;
|
||||||
|
uint8_t sendseed_sent_key[BENCRYPTION_MAX_KEY_SIZE];
|
||||||
|
uint8_t sendseed_sent_iv[BENCRYPTION_MAX_BLOCK_SIZE];
|
||||||
|
uint16_t pending_recvseed_id;
|
||||||
|
BPending job_send_seed;
|
||||||
|
} udp;
|
||||||
|
struct {
|
||||||
|
StreamPeerIO pio;
|
||||||
|
} tcp;
|
||||||
|
} pio;
|
||||||
|
|
||||||
|
// link sending
|
||||||
|
DataProtoSink send_dp;
|
||||||
|
|
||||||
|
// relaying objects
|
||||||
|
struct peer_data *relaying_peer; // peer through which we are relaying, or NULL
|
||||||
|
LinkedList1Node relaying_list_node; // node in relay peer's relay_users
|
||||||
|
|
||||||
|
// waiting for relay data
|
||||||
|
int waiting_relay;
|
||||||
|
LinkedList1Node waiting_relay_list_node;
|
||||||
|
|
||||||
|
// retry timer
|
||||||
|
BTimer reset_timer;
|
||||||
|
|
||||||
|
// relay server specific
|
||||||
|
int is_relay;
|
||||||
|
LinkedList1Node relay_list_node;
|
||||||
|
LinkedList1 relay_users;
|
||||||
|
|
||||||
|
// binding state
|
||||||
|
int binding;
|
||||||
|
int binding_addrpos;
|
||||||
|
|
||||||
|
// peers linked list node
|
||||||
|
LinkedList1Node list_node;
|
||||||
|
};
|
|
@ -0,0 +1,22 @@
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,52 @@
|
||||||
|
# - Try to find the GLIB2 libraries
|
||||||
|
# Once done this will define
|
||||||
|
#
|
||||||
|
# GLIB2_FOUND - system has glib2
|
||||||
|
# GLIB2_INCLUDE_DIR - the glib2 include directory
|
||||||
|
# GLIB2_LIBRARIES - glib2 library
|
||||||
|
|
||||||
|
# Copyright (c) 2008 Laurent Montel, <montel@kde.org>
|
||||||
|
#
|
||||||
|
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||||
|
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||||
|
|
||||||
|
|
||||||
|
if(GLIB2_INCLUDE_DIR AND GLIB2_LIBRARIES)
|
||||||
|
# Already in cache, be silent
|
||||||
|
set(GLIB2_FIND_QUIETLY TRUE)
|
||||||
|
endif(GLIB2_INCLUDE_DIR AND GLIB2_LIBRARIES)
|
||||||
|
|
||||||
|
find_package(PkgConfig)
|
||||||
|
pkg_check_modules(PC_LibGLIB2 QUIET glib-2.0)
|
||||||
|
|
||||||
|
find_path(GLIB2_MAIN_INCLUDE_DIR
|
||||||
|
NAMES glib.h
|
||||||
|
HINTS ${PC_LibGLIB2_INCLUDEDIR}
|
||||||
|
PATH_SUFFIXES glib-2.0)
|
||||||
|
|
||||||
|
find_library(GLIB2_LIBRARY
|
||||||
|
NAMES glib-2.0
|
||||||
|
HINTS ${PC_LibGLIB2_LIBDIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(GLIB2_LIBRARIES ${GLIB2_LIBRARY})
|
||||||
|
|
||||||
|
# search the glibconfig.h include dir under the same root where the library is found
|
||||||
|
get_filename_component(glib2LibDir "${GLIB2_LIBRARIES}" PATH)
|
||||||
|
|
||||||
|
find_path(GLIB2_INTERNAL_INCLUDE_DIR glibconfig.h
|
||||||
|
PATH_SUFFIXES glib-2.0/include
|
||||||
|
HINTS ${PC_LibGLIB2_INCLUDEDIR} "${glib2LibDir}" ${CMAKE_SYSTEM_LIBRARY_PATH})
|
||||||
|
|
||||||
|
set(GLIB2_INCLUDE_DIR "${GLIB2_MAIN_INCLUDE_DIR}")
|
||||||
|
|
||||||
|
# not sure if this include dir is optional or required
|
||||||
|
# for now it is optional
|
||||||
|
if(GLIB2_INTERNAL_INCLUDE_DIR)
|
||||||
|
set(GLIB2_INCLUDE_DIR ${GLIB2_INCLUDE_DIR} "${GLIB2_INTERNAL_INCLUDE_DIR}")
|
||||||
|
endif(GLIB2_INTERNAL_INCLUDE_DIR)
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(GLIB2 DEFAULT_MSG GLIB2_LIBRARIES GLIB2_MAIN_INCLUDE_DIR)
|
||||||
|
|
||||||
|
mark_as_advanced(GLIB2_INCLUDE_DIR GLIB2_LIBRARIES)
|
|
@ -0,0 +1,113 @@
|
||||||
|
#
|
||||||
|
# FIND_LIBRARY_WITH_DEBUG
|
||||||
|
# -> enhanced FIND_LIBRARY to allow the search for an
|
||||||
|
# optional debug library with a WIN32_DEBUG_POSTFIX similar
|
||||||
|
# to CMAKE_DEBUG_POSTFIX when creating a shared lib
|
||||||
|
# it has to be the second and third argument
|
||||||
|
|
||||||
|
# Copyright (c) 2007, Christian Ehrlicher, <ch.ehrlicher@gmx.de>
|
||||||
|
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||||
|
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||||
|
|
||||||
|
MACRO(FIND_LIBRARY_WITH_DEBUG var_name win32_dbg_postfix_name dgb_postfix libname)
|
||||||
|
|
||||||
|
IF(NOT "${win32_dbg_postfix_name}" STREQUAL "WIN32_DEBUG_POSTFIX")
|
||||||
|
|
||||||
|
# no WIN32_DEBUG_POSTFIX -> simply pass all arguments to FIND_LIBRARY
|
||||||
|
FIND_LIBRARY(${var_name}
|
||||||
|
${win32_dbg_postfix_name}
|
||||||
|
${dgb_postfix}
|
||||||
|
${libname}
|
||||||
|
${ARGN}
|
||||||
|
)
|
||||||
|
|
||||||
|
ELSE(NOT "${win32_dbg_postfix_name}" STREQUAL "WIN32_DEBUG_POSTFIX")
|
||||||
|
|
||||||
|
IF(NOT WIN32)
|
||||||
|
# on non-win32 we don't need to take care about WIN32_DEBUG_POSTFIX
|
||||||
|
|
||||||
|
FIND_LIBRARY(${var_name} ${libname} ${ARGN})
|
||||||
|
|
||||||
|
ELSE(NOT WIN32)
|
||||||
|
|
||||||
|
# 1. get all possible libnames
|
||||||
|
SET(args ${ARGN})
|
||||||
|
SET(newargs "")
|
||||||
|
SET(libnames_release "")
|
||||||
|
SET(libnames_debug "")
|
||||||
|
|
||||||
|
LIST(LENGTH args listCount)
|
||||||
|
|
||||||
|
IF("${libname}" STREQUAL "NAMES")
|
||||||
|
SET(append_rest 0)
|
||||||
|
LIST(APPEND args " ")
|
||||||
|
|
||||||
|
FOREACH(i RANGE ${listCount})
|
||||||
|
LIST(GET args ${i} val)
|
||||||
|
|
||||||
|
IF(append_rest)
|
||||||
|
LIST(APPEND newargs ${val})
|
||||||
|
ELSE(append_rest)
|
||||||
|
IF("${val}" STREQUAL "PATHS")
|
||||||
|
LIST(APPEND newargs ${val})
|
||||||
|
SET(append_rest 1)
|
||||||
|
ELSE("${val}" STREQUAL "PATHS")
|
||||||
|
LIST(APPEND libnames_release "${val}")
|
||||||
|
LIST(APPEND libnames_debug "${val}${dgb_postfix}")
|
||||||
|
ENDIF("${val}" STREQUAL "PATHS")
|
||||||
|
ENDIF(append_rest)
|
||||||
|
|
||||||
|
ENDFOREACH(i)
|
||||||
|
|
||||||
|
ELSE("${libname}" STREQUAL "NAMES")
|
||||||
|
|
||||||
|
# just one name
|
||||||
|
LIST(APPEND libnames_release "${libname}")
|
||||||
|
LIST(APPEND libnames_debug "${libname}${dgb_postfix}")
|
||||||
|
|
||||||
|
SET(newargs ${args})
|
||||||
|
|
||||||
|
ENDIF("${libname}" STREQUAL "NAMES")
|
||||||
|
|
||||||
|
# search the release lib
|
||||||
|
FIND_LIBRARY(${var_name}_RELEASE
|
||||||
|
NAMES ${libnames_release}
|
||||||
|
${newargs}
|
||||||
|
)
|
||||||
|
|
||||||
|
# search the debug lib
|
||||||
|
FIND_LIBRARY(${var_name}_DEBUG
|
||||||
|
NAMES ${libnames_debug}
|
||||||
|
${newargs}
|
||||||
|
)
|
||||||
|
|
||||||
|
IF(${var_name}_RELEASE AND ${var_name}_DEBUG)
|
||||||
|
|
||||||
|
# both libs found
|
||||||
|
SET(${var_name} optimized ${${var_name}_RELEASE}
|
||||||
|
debug ${${var_name}_DEBUG})
|
||||||
|
|
||||||
|
ELSE(${var_name}_RELEASE AND ${var_name}_DEBUG)
|
||||||
|
|
||||||
|
IF(${var_name}_RELEASE)
|
||||||
|
|
||||||
|
# only release found
|
||||||
|
SET(${var_name} ${${var_name}_RELEASE})
|
||||||
|
|
||||||
|
ELSE(${var_name}_RELEASE)
|
||||||
|
|
||||||
|
# only debug (or nothing) found
|
||||||
|
SET(${var_name} ${${var_name}_DEBUG})
|
||||||
|
|
||||||
|
ENDIF(${var_name}_RELEASE)
|
||||||
|
|
||||||
|
ENDIF(${var_name}_RELEASE AND ${var_name}_DEBUG)
|
||||||
|
|
||||||
|
MARK_AS_ADVANCED(${var_name}_RELEASE)
|
||||||
|
MARK_AS_ADVANCED(${var_name}_DEBUG)
|
||||||
|
|
||||||
|
ENDIF(NOT WIN32)
|
||||||
|
|
||||||
|
ENDIF(NOT "${win32_dbg_postfix_name}" STREQUAL "WIN32_DEBUG_POSTFIX")
|
||||||
|
|
||||||
|
ENDMACRO(FIND_LIBRARY_WITH_DEBUG)
|
|
@ -0,0 +1,57 @@
|
||||||
|
# - Try to find the NSPR library
|
||||||
|
# Once done this will define
|
||||||
|
#
|
||||||
|
# NSPR_FOUND - system has the NSPR library
|
||||||
|
# NSPR_INCLUDE_DIRS - Include paths needed
|
||||||
|
# NSPR_LIBRARY_DIRS - Linker paths needed
|
||||||
|
# NSPR_LIBRARIES - Libraries needed
|
||||||
|
|
||||||
|
# Copyright (c) 2010, Ambroz Bizjak, <ambrop7@gmail.com>
|
||||||
|
#
|
||||||
|
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||||
|
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||||
|
|
||||||
|
include(FindLibraryWithDebug)
|
||||||
|
|
||||||
|
if (NSPR_LIBRARIES)
|
||||||
|
set(NSPR_FIND_QUIETLY TRUE)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(NSPR_FOUND FALSE)
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
find_path(NSPR_FIND_INCLUDE_DIR prerror.h)
|
||||||
|
|
||||||
|
FIND_LIBRARY_WITH_DEBUG(NSPR_FIND_LIBRARIES_PLDS WIN32_DEBUG_POSTFIX d NAMES plds4 libplds4)
|
||||||
|
FIND_LIBRARY_WITH_DEBUG(NSPR_FIND_LIBRARIES_PLC WIN32_DEBUG_POSTFIX d NAMES plc4 libplc4)
|
||||||
|
FIND_LIBRARY_WITH_DEBUG(NSPR_FIND_LIBRARIES_NSPR WIN32_DEBUG_POSTFIX d NAMES nspr4 libnspr4)
|
||||||
|
|
||||||
|
if (NSPR_FIND_INCLUDE_DIR AND NSPR_FIND_LIBRARIES_PLDS AND NSPR_FIND_LIBRARIES_PLC AND NSPR_FIND_LIBRARIES_NSPR)
|
||||||
|
set(NSPR_FOUND TRUE)
|
||||||
|
set(NSPR_INCLUDE_DIRS "${NSPR_FIND_INCLUDE_DIR}" CACHE STRING "NSPR include dirs")
|
||||||
|
set(NSPR_LIBRARY_DIRS "" CACHE STRING "NSPR library dirs")
|
||||||
|
set(NSPR_LIBRARIES "${NSPR_FIND_LIBRARIES_PLDS};${NSPR_FIND_LIBRARIES_PLC};${NSPR_FIND_LIBRARIES_NSPR}" CACHE STRING "NSPR libraries")
|
||||||
|
endif ()
|
||||||
|
else ()
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_check_modules(NSPR_PC nspr)
|
||||||
|
|
||||||
|
if (NSPR_PC_FOUND)
|
||||||
|
set(NSPR_FOUND TRUE)
|
||||||
|
set(NSPR_INCLUDE_DIRS "${NSPR_PC_INCLUDE_DIRS}" CACHE STRING "NSPR include dirs")
|
||||||
|
set(NSPR_LIBRARY_DIRS "${NSPR_PC_LIBRARY_DIRS}" CACHE STRING "NSPR library dirs")
|
||||||
|
set(NSPR_LIBRARIES "${NSPR_PC_LIBRARIES}" CACHE STRING "NSPR libraries")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (NSPR_FOUND)
|
||||||
|
if (NOT NSPR_FIND_QUIETLY)
|
||||||
|
MESSAGE(STATUS "Found NSPR: ${NSPR_INCLUDE_DIRS} ${NSPR_LIBRARY_DIRS} ${NSPR_LIBRARIES}")
|
||||||
|
endif ()
|
||||||
|
else ()
|
||||||
|
if (NSPR_FIND_REQUIRED)
|
||||||
|
message(FATAL_ERROR "Could NOT find NSPR")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
mark_as_advanced(NSPR_INCLUDE_DIRS NSPR_LIBRARY_DIRS NSPR_LIBRARIES)
|
|
@ -0,0 +1,57 @@
|
||||||
|
# - Try to find the NSS library
|
||||||
|
# Once done this will define
|
||||||
|
#
|
||||||
|
# NSS_FOUND - system has the NSS library
|
||||||
|
# NSS_INCLUDE_DIRS - Include paths needed
|
||||||
|
# NSS_LIBRARY_DIRS - Linker paths needed
|
||||||
|
# NSS_LIBRARIES - Libraries needed
|
||||||
|
|
||||||
|
# Copyright (c) 2010, Ambroz Bizjak, <ambrop7@gmail.com>
|
||||||
|
#
|
||||||
|
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||||
|
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||||
|
|
||||||
|
include(FindLibraryWithDebug)
|
||||||
|
|
||||||
|
if (NSS_LIBRARIES)
|
||||||
|
set(NSS_FIND_QUIETLY TRUE)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(NSS_FOUND FALSE)
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
find_path(NSS_FIND_INCLUDE_DIR nss.h)
|
||||||
|
|
||||||
|
FIND_LIBRARY_WITH_DEBUG(NSS_FIND_LIBRARIES_SSL WIN32_DEBUG_POSTFIX d NAMES ssl3)
|
||||||
|
FIND_LIBRARY_WITH_DEBUG(NSS_FIND_LIBRARIES_SMIME WIN32_DEBUG_POSTFIX d NAMES smime3)
|
||||||
|
FIND_LIBRARY_WITH_DEBUG(NSS_FIND_LIBRARIES_NSS WIN32_DEBUG_POSTFIX d NAMES nss3)
|
||||||
|
|
||||||
|
if (NSS_FIND_INCLUDE_DIR AND NSS_FIND_LIBRARIES_SSL AND NSS_FIND_LIBRARIES_SMIME AND NSS_FIND_LIBRARIES_NSS)
|
||||||
|
set(NSS_FOUND TRUE)
|
||||||
|
set(NSS_INCLUDE_DIRS "${NSS_FIND_INCLUDE_DIR}" CACHE STRING "NSS include dirs")
|
||||||
|
set(NSS_LIBRARY_DIRS "" CACHE STRING "NSS library dirs")
|
||||||
|
set(NSS_LIBRARIES "${NSS_FIND_LIBRARIES_SSL};${NSS_FIND_LIBRARIES_SMIME};${NSS_FIND_LIBRARIES_NSS}" CACHE STRING "NSS libraries")
|
||||||
|
endif ()
|
||||||
|
else ()
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_check_modules(NSS_PC nss)
|
||||||
|
|
||||||
|
if (NSS_PC_FOUND)
|
||||||
|
set(NSS_FOUND TRUE)
|
||||||
|
set(NSS_INCLUDE_DIRS "${NSS_PC_INCLUDE_DIRS}" CACHE STRING "NSS include dirs")
|
||||||
|
set(NSS_LIBRARY_DIRS "${NSS_PC_LIBRARY_DIRS}" CACHE STRING "NSS library dirs")
|
||||||
|
set(NSS_LIBRARIES "${NSS_PC_LIBRARIES}" CACHE STRING "NSS libraries")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (NSS_FOUND)
|
||||||
|
if (NOT NSS_FIND_QUIETLY)
|
||||||
|
MESSAGE(STATUS "Found NSS: ${NSS_INCLUDE_DIRS} ${NSS_LIBRARY_DIRS} ${NSS_LIBRARIES}")
|
||||||
|
endif ()
|
||||||
|
else ()
|
||||||
|
if (NSS_FIND_REQUIRED)
|
||||||
|
message(FATAL_ERROR "Could NOT find NSS")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
mark_as_advanced(NSS_INCLUDE_DIRS NSS_LIBRARY_DIRS NSS_LIBRARIES)
|
|
@ -0,0 +1,72 @@
|
||||||
|
# - Try to find the OpenSSL library
|
||||||
|
# Once done this will define
|
||||||
|
#
|
||||||
|
# OpenSSL_FOUND - system has the OpenSSL library
|
||||||
|
# OpenSSL_INCLUDE_DIRS - Include paths needed
|
||||||
|
# OpenSSL_LIBRARY_DIRS - Linker paths needed
|
||||||
|
# OpenSSL_LIBRARIES - Libraries needed
|
||||||
|
|
||||||
|
# Copyright (c) 2010, Ambroz Bizjak, <ambrop7@gmail.com>
|
||||||
|
#
|
||||||
|
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||||
|
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||||
|
|
||||||
|
include(FindLibraryWithDebug)
|
||||||
|
|
||||||
|
if (OpenSSL_LIBRARIES)
|
||||||
|
set(OpenSSL_FIND_QUIETLY TRUE)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(OpenSSL_FOUND FALSE)
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
find_path(OpenSSL_FIND_INCLUDE_DIR openssl/ssl.h)
|
||||||
|
|
||||||
|
if (OpenSSL_FIND_INCLUDE_DIR)
|
||||||
|
# look for libraries built with GCC
|
||||||
|
find_library(OpenSSL_FIND_LIBRARIES_SSL NAMES ssl)
|
||||||
|
find_library(OpenSSL_FIND_LIBRARIES_CRYPTO NAMES crypto)
|
||||||
|
|
||||||
|
if (OpenSSL_FIND_LIBRARIES_SSL AND OpenSSL_FIND_LIBRARIES_CRYPTO)
|
||||||
|
set(OpenSSL_FOUND TRUE)
|
||||||
|
set(OpenSSL_LIBRARY_DIRS "" CACHE STRING "OpenSSL library dirs")
|
||||||
|
set(OpenSSL_LIBRARIES "${OpenSSL_FIND_LIBRARIES_SSL};${OpenSSL_FIND_LIBRARIES_CRYPTO}" CACHE STRING "OpenSSL libraries")
|
||||||
|
else ()
|
||||||
|
# look for libraries built with MSVC
|
||||||
|
FIND_LIBRARY_WITH_DEBUG(OpenSSL_FIND_LIBRARIES_SSL WIN32_DEBUG_POSTFIX d NAMES ssl ssleay ssleay32 libssleay32 ssleay32MD)
|
||||||
|
FIND_LIBRARY_WITH_DEBUG(OpenSSL_FIND_LIBRARIES_EAY WIN32_DEBUG_POSTFIX d NAMES eay libeay libeay32 libeay32MD)
|
||||||
|
|
||||||
|
if (OpenSSL_FIND_LIBRARIES_SSL AND OpenSSL_FIND_LIBRARIES_EAY)
|
||||||
|
set(OpenSSL_FOUND TRUE)
|
||||||
|
set(OpenSSL_LIBRARY_DIRS "" CACHE STRING "OpenSSL library dirs")
|
||||||
|
set(OpenSSL_LIBRARIES "${OpenSSL_FIND_LIBRARIES_SSL};${OpenSSL_FIND_LIBRARIES_EAY}" CACHE STRING "OpenSSL libraries")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (OpenSSL_FOUND)
|
||||||
|
set(OpenSSL_INCLUDE_DIRS "${OpenSSL_FIND_INCLUDE_DIR}" CACHE STRING "OpenSSL include dirs")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
else ()
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_check_modules(OpenSSL_PC openssl)
|
||||||
|
|
||||||
|
if (OpenSSL_PC_FOUND)
|
||||||
|
set(OpenSSL_FOUND TRUE)
|
||||||
|
set(OpenSSL_INCLUDE_DIRS "${OpenSSL_PC_INCLUDE_DIRS}" CACHE STRING "OpenSSL include dirs")
|
||||||
|
set(OpenSSL_LIBRARY_DIRS "${OpenSSL_PC_LIBRARY_DIRS}" CACHE STRING "OpenSSL library dirs")
|
||||||
|
set(OpenSSL_LIBRARIES "${OpenSSL_PC_LIBRARIES}" CACHE STRING "OpenSSL libraries")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (OpenSSL_FOUND)
|
||||||
|
if (NOT OpenSSL_FIND_QUIETLY)
|
||||||
|
MESSAGE(STATUS "Found OpenSSL: ${OpenSSL_INCLUDE_DIRS} ${OpenSSL_LIBRARY_DIRS} ${OpenSSL_LIBRARIES}")
|
||||||
|
endif ()
|
||||||
|
else ()
|
||||||
|
if (OpenSSL_FIND_REQUIRED)
|
||||||
|
message(FATAL_ERROR "Could NOT find OpenSSL")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
mark_as_advanced(OpenSSL_INCLUDE_DIRS OpenSSL_LIBRARY_DIRS OpenSSL_LIBRARIES)
|
|
@ -0,0 +1,112 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Compiles tun2socks for Linux.
|
||||||
|
# Intended as a convenience if you don't want to deal with CMake.
|
||||||
|
|
||||||
|
# Input environment vars:
|
||||||
|
# SRCDIR - BadVPN source code
|
||||||
|
# CC - compiler
|
||||||
|
# CFLAGS - compiler compile flags
|
||||||
|
# LDFLAGS - compiler link flags
|
||||||
|
# ENDIAN - "little" or "big"
|
||||||
|
# KERNEL - "2.6" or "2.4", default "2.6"
|
||||||
|
#
|
||||||
|
# Puts object files and the executable in the working directory.
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ -z $SRCDIR ]] || [[ ! -e $SRCDIR/CMakeLists.txt ]]; then
|
||||||
|
echo "SRCDIR is wrong"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! "${CC}" --version &>/dev/null; then
|
||||||
|
echo "CC is wrong"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $ENDIAN != "little" ]] && [[ $ENDIAN != "big" ]]; then
|
||||||
|
echo "ENDIAN is wrong"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z $KERNEL ]]; then
|
||||||
|
KERNEL="2.6"
|
||||||
|
elif [[ $KERNEL != "2.6" ]] && [[ $KERNEL != "2.4" ]]; then
|
||||||
|
echo "KERNEL is wrong"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
CFLAGS="${CFLAGS} -std=gnu99"
|
||||||
|
INCLUDES=( "-I${SRCDIR}" "-I${SRCDIR}/lwip/src/include/ipv4" "-I${SRCDIR}/lwip/src/include/ipv6" "-I${SRCDIR}/lwip/src/include" "-I${SRCDIR}/lwip/custom" )
|
||||||
|
DEFS=( -DBADVPN_THREAD_SAFE=0 -DBADVPN_LINUX -DBADVPN_BREACTOR_BADVPN -D_GNU_SOURCE )
|
||||||
|
|
||||||
|
[[ $KERNEL = "2.4" ]] && DEFS=( "${DEFS[@]}" -DBADVPN_USE_SELFPIPE -DBADVPN_USE_POLL ) || DEFS=( "${DEFS[@]}" -DBADVPN_USE_SIGNALFD -DBADVPN_USE_EPOLL )
|
||||||
|
|
||||||
|
[[ $ENDIAN = "little" ]] && DEFS=( "${DEFS[@]}" -DBADVPN_LITTLE_ENDIAN ) || DEFS=( "${DEFS[@]}" -DBADVPN_BIG_ENDIAN )
|
||||||
|
|
||||||
|
SOURCES="
|
||||||
|
base/BLog_syslog.c
|
||||||
|
system/BReactor_badvpn.c
|
||||||
|
system/BSignal.c
|
||||||
|
system/BConnection_unix.c
|
||||||
|
system/BTime.c
|
||||||
|
system/BUnixSignal.c
|
||||||
|
system/BNetwork.c
|
||||||
|
flow/StreamRecvInterface.c
|
||||||
|
flow/PacketRecvInterface.c
|
||||||
|
flow/PacketPassInterface.c
|
||||||
|
flow/StreamPassInterface.c
|
||||||
|
flow/SinglePacketBuffer.c
|
||||||
|
flow/BufferWriter.c
|
||||||
|
flow/PacketBuffer.c
|
||||||
|
flow/PacketStreamSender.c
|
||||||
|
flow/PacketPassConnector.c
|
||||||
|
flow/PacketProtoFlow.c
|
||||||
|
flow/PacketPassFairQueue.c
|
||||||
|
flow/PacketProtoEncoder.c
|
||||||
|
flow/PacketProtoDecoder.c
|
||||||
|
socksclient/BSocksClient.c
|
||||||
|
tuntap/BTap.c
|
||||||
|
lwip/src/core/timers.c
|
||||||
|
lwip/src/core/udp.c
|
||||||
|
lwip/src/core/memp.c
|
||||||
|
lwip/src/core/init.c
|
||||||
|
lwip/src/core/pbuf.c
|
||||||
|
lwip/src/core/tcp.c
|
||||||
|
lwip/src/core/tcp_out.c
|
||||||
|
lwip/src/core/netif.c
|
||||||
|
lwip/src/core/def.c
|
||||||
|
lwip/src/core/mem.c
|
||||||
|
lwip/src/core/tcp_in.c
|
||||||
|
lwip/src/core/stats.c
|
||||||
|
lwip/src/core/inet_chksum.c
|
||||||
|
lwip/src/core/ipv4/icmp.c
|
||||||
|
lwip/src/core/ipv4/ip4.c
|
||||||
|
lwip/src/core/ipv4/ip4_addr.c
|
||||||
|
lwip/src/core/ipv4/ip_frag.c
|
||||||
|
lwip/src/core/ipv6/ip6.c
|
||||||
|
lwip/src/core/ipv6/nd6.c
|
||||||
|
lwip/src/core/ipv6/icmp6.c
|
||||||
|
lwip/src/core/ipv6/ip6_addr.c
|
||||||
|
lwip/src/core/ipv6/ip6_frag.c
|
||||||
|
lwip/custom/sys.c
|
||||||
|
tun2socks/tun2socks.c
|
||||||
|
base/DebugObject.c
|
||||||
|
base/BLog.c
|
||||||
|
base/BPending.c
|
||||||
|
flowextra/PacketPassInactivityMonitor.c
|
||||||
|
tun2socks/SocksUdpGwClient.c
|
||||||
|
udpgw_client/UdpGwClient.c
|
||||||
|
"
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
OBJS=()
|
||||||
|
for f in $SOURCES; do
|
||||||
|
obj=$(basename "${f}").o
|
||||||
|
"${CC}" -c ${CFLAGS} "${INCLUDES[@]}" "${DEFS[@]}" "${SRCDIR}/${f}" -o "${obj}"
|
||||||
|
OBJS=( "${OBJS[@]}" "${obj}" )
|
||||||
|
done
|
||||||
|
|
||||||
|
"${CC}" ${LDFLAGS} "${OBJS[@]}" -o tun2socks -lrt
|
|
@ -0,0 +1,84 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Compiles udpgw for Linux.
|
||||||
|
# Intended as a convenience if you don't want to deal with CMake.
|
||||||
|
|
||||||
|
# Input environment vars:
|
||||||
|
# SRCDIR - BadVPN source code
|
||||||
|
# CC - compiler
|
||||||
|
# CFLAGS - compiler compile flags
|
||||||
|
# LDFLAGS - compiler link flags
|
||||||
|
# ENDIAN - "little" or "big"
|
||||||
|
# KERNEL - "2.6" or "2.4", default "2.6"
|
||||||
|
#
|
||||||
|
# Puts object files and the executable in the working directory.
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ -z $SRCDIR ]] || [[ ! -e $SRCDIR/CMakeLists.txt ]]; then
|
||||||
|
echo "SRCDIR is wrong"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! "${CC}" --version &>/dev/null; then
|
||||||
|
echo "CC is wrong"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $ENDIAN != "little" ]] && [[ $ENDIAN != "big" ]]; then
|
||||||
|
echo "ENDIAN is wrong"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z $KERNEL ]]; then
|
||||||
|
KERNEL="2.6"
|
||||||
|
elif [[ $KERNEL != "2.6" ]] && [[ $KERNEL != "2.4" ]]; then
|
||||||
|
echo "KERNEL is wrong"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
CFLAGS="${CFLAGS} -std=gnu99"
|
||||||
|
INCLUDES=( "-I${SRCDIR}" )
|
||||||
|
DEFS=( -DBADVPN_THREAD_SAFE=0 -DBADVPN_LINUX -DBADVPN_BREACTOR_BADVPN -D_GNU_SOURCE )
|
||||||
|
|
||||||
|
[[ $KERNEL = "2.4" ]] && DEFS=( "${DEFS[@]}" -DBADVPN_USE_SELFPIPE -DBADVPN_USE_POLL ) || DEFS=( "${DEFS[@]}" -DBADVPN_USE_SIGNALFD -DBADVPN_USE_EPOLL )
|
||||||
|
|
||||||
|
[[ $ENDIAN = "little" ]] && DEFS=( "${DEFS[@]}" -DBADVPN_LITTLE_ENDIAN ) || DEFS=( "${DEFS[@]}" -DBADVPN_BIG_ENDIAN )
|
||||||
|
|
||||||
|
SOURCES="
|
||||||
|
base/BLog_syslog.c
|
||||||
|
system/BReactor_badvpn.c
|
||||||
|
system/BSignal.c
|
||||||
|
system/BConnection_unix.c
|
||||||
|
system/BDatagram_unix.c
|
||||||
|
system/BTime.c
|
||||||
|
system/BUnixSignal.c
|
||||||
|
system/BNetwork.c
|
||||||
|
flow/StreamRecvInterface.c
|
||||||
|
flow/PacketRecvInterface.c
|
||||||
|
flow/PacketPassInterface.c
|
||||||
|
flow/StreamPassInterface.c
|
||||||
|
flow/SinglePacketBuffer.c
|
||||||
|
flow/BufferWriter.c
|
||||||
|
flow/PacketBuffer.c
|
||||||
|
flow/PacketStreamSender.c
|
||||||
|
flow/PacketProtoFlow.c
|
||||||
|
flow/PacketPassFairQueue.c
|
||||||
|
flow/PacketProtoEncoder.c
|
||||||
|
flow/PacketProtoDecoder.c
|
||||||
|
base/DebugObject.c
|
||||||
|
base/BLog.c
|
||||||
|
base/BPending.c
|
||||||
|
udpgw/udpgw.c
|
||||||
|
"
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
OBJS=()
|
||||||
|
for f in $SOURCES; do
|
||||||
|
obj=$(basename "${f}").o
|
||||||
|
"${CC}" -c ${CFLAGS} "${INCLUDES[@]}" "${DEFS[@]}" "${SRCDIR}/${f}" -o "${obj}"
|
||||||
|
OBJS=( "${OBJS[@]}" "${obj}" )
|
||||||
|
done
|
||||||
|
|
||||||
|
"${CC}" ${LDFLAGS} "${OBJS[@]}" -o udpgw -lrt
|
|
@ -0,0 +1,340 @@
|
||||||
|
/**
|
||||||
|
* @file BDHCPClient.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <net/if_arp.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <linux/filter.h>
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <misc/byteorder.h>
|
||||||
|
#include <misc/ethernet_proto.h>
|
||||||
|
#include <misc/ipv4_proto.h>
|
||||||
|
#include <misc/udp_proto.h>
|
||||||
|
#include <misc/dhcp_proto.h>
|
||||||
|
#include <misc/get_iface_info.h>
|
||||||
|
#include <base/BLog.h>
|
||||||
|
|
||||||
|
#include <dhcpclient/BDHCPClient.h>
|
||||||
|
|
||||||
|
#include <generated/blog_channel_BDHCPClient.h>
|
||||||
|
|
||||||
|
#define DHCP_SERVER_PORT 67
|
||||||
|
#define DHCP_CLIENT_PORT 68
|
||||||
|
|
||||||
|
#define IPUDP_OVERHEAD (sizeof(struct ipv4_header) + sizeof(struct udp_header))
|
||||||
|
|
||||||
|
static const struct sock_filter dhcp_sock_filter[] = {
|
||||||
|
BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 9), // A <- IP protocol
|
||||||
|
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPV4_PROTOCOL_UDP, 0, 3), // IP protocol = UDP ?
|
||||||
|
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 22), // A <- UDP destination port
|
||||||
|
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_CLIENT_PORT, 0, 1), // UDP destination port = DHCP client ?
|
||||||
|
BPF_STMT(BPF_RET + BPF_K, 65535), // return all
|
||||||
|
BPF_STMT(BPF_RET + BPF_K, 0) // ignore
|
||||||
|
};
|
||||||
|
|
||||||
|
static void dgram_handler (BDHCPClient *o, int event)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
BLog(BLOG_ERROR, "packet socket error");
|
||||||
|
|
||||||
|
// report error
|
||||||
|
DEBUGERROR(&o->d_err, o->handler(o->user, BDHCPCLIENT_EVENT_ERROR));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dhcp_handler (BDHCPClient *o, int event)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case BDHCPCLIENTCORE_EVENT_UP:
|
||||||
|
ASSERT(!o->up)
|
||||||
|
o->up = 1;
|
||||||
|
o->handler(o->user, BDHCPCLIENT_EVENT_UP);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case BDHCPCLIENTCORE_EVENT_DOWN:
|
||||||
|
ASSERT(o->up)
|
||||||
|
o->up = 0;
|
||||||
|
o->handler(o->user, BDHCPCLIENT_EVENT_DOWN);
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dhcp_func_getsendermac (BDHCPClient *o, uint8_t *out_mac)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
BAddr remote_addr;
|
||||||
|
BIPAddr local_addr;
|
||||||
|
if (!BDatagram_GetLastReceiveAddrs(&o->dgram, &remote_addr, &local_addr)) {
|
||||||
|
BLog(BLOG_ERROR, "BDatagram_GetLastReceiveAddrs failed");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remote_addr.type != BADDR_TYPE_PACKET) {
|
||||||
|
BLog(BLOG_ERROR, "address type invalid");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remote_addr.packet.header_type != BADDR_PACKET_HEADER_TYPE_ETHERNET) {
|
||||||
|
BLog(BLOG_ERROR, "address header type invalid");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(out_mac, remote_addr.packet.phys_addr, 6);
|
||||||
|
return;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
memset(out_mac, 0, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
int BDHCPClient_Init (BDHCPClient *o, const char *ifname, struct BDHCPClient_opts opts, BReactor *reactor, BRandom2 *random2, BDHCPClient_handler handler, void *user)
|
||||||
|
{
|
||||||
|
// init arguments
|
||||||
|
o->reactor = reactor;
|
||||||
|
o->handler = handler;
|
||||||
|
o->user = user;
|
||||||
|
|
||||||
|
// get interface information
|
||||||
|
uint8_t if_mac[6];
|
||||||
|
int if_mtu;
|
||||||
|
int if_index;
|
||||||
|
if (!badvpn_get_iface_info(ifname, if_mac, &if_mtu, &if_index)) {
|
||||||
|
BLog(BLOG_ERROR, "failed to get interface information");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BLog(BLOG_INFO, "if_mac=%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8" if_mtu=%d if_index=%d",
|
||||||
|
if_mac[0], if_mac[1], if_mac[2], if_mac[3], if_mac[4], if_mac[5], if_mtu, if_index);
|
||||||
|
|
||||||
|
if (if_mtu < IPUDP_OVERHEAD) {
|
||||||
|
BLog(BLOG_ERROR, "MTU is too small for UDP/IP !?!");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dhcp_mtu = if_mtu - IPUDP_OVERHEAD;
|
||||||
|
|
||||||
|
// init dgram
|
||||||
|
if (!BDatagram_Init(&o->dgram, BADDR_TYPE_PACKET, o->reactor, o, (BDatagram_handler)dgram_handler)) {
|
||||||
|
BLog(BLOG_ERROR, "BDatagram_Init failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set socket filter
|
||||||
|
{
|
||||||
|
struct sock_filter filter[sizeof(dhcp_sock_filter) / sizeof(dhcp_sock_filter[0])];
|
||||||
|
memcpy(filter, dhcp_sock_filter, sizeof(filter));
|
||||||
|
struct sock_fprog fprog = {
|
||||||
|
.len = sizeof(filter) / sizeof(filter[0]),
|
||||||
|
.filter = filter
|
||||||
|
};
|
||||||
|
if (setsockopt(BDatagram_GetFd(&o->dgram), SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0) {
|
||||||
|
BLog(BLOG_NOTICE, "not using socket filter");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bind dgram
|
||||||
|
BAddr bind_addr;
|
||||||
|
BAddr_InitPacket(&bind_addr, hton16(ETHERTYPE_IPV4), if_index, BADDR_PACKET_HEADER_TYPE_ETHERNET, BADDR_PACKET_PACKET_TYPE_HOST, if_mac);
|
||||||
|
if (!BDatagram_Bind(&o->dgram, bind_addr)) {
|
||||||
|
BLog(BLOG_ERROR, "BDatagram_Bind failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set dgram send addresses
|
||||||
|
BAddr dest_addr;
|
||||||
|
uint8_t broadcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
||||||
|
BAddr_InitPacket(&dest_addr, hton16(ETHERTYPE_IPV4), if_index, BADDR_PACKET_HEADER_TYPE_ETHERNET, BADDR_PACKET_PACKET_TYPE_BROADCAST, broadcast_mac);
|
||||||
|
BIPAddr local_addr;
|
||||||
|
BIPAddr_InitInvalid(&local_addr);
|
||||||
|
BDatagram_SetSendAddrs(&o->dgram, dest_addr, local_addr);
|
||||||
|
|
||||||
|
// init dgram interfaces
|
||||||
|
BDatagram_SendAsync_Init(&o->dgram, if_mtu);
|
||||||
|
BDatagram_RecvAsync_Init(&o->dgram, if_mtu);
|
||||||
|
|
||||||
|
// init sending
|
||||||
|
|
||||||
|
// init copier
|
||||||
|
PacketCopier_Init(&o->send_copier, dhcp_mtu, BReactor_PendingGroup(o->reactor));
|
||||||
|
|
||||||
|
// init encoder
|
||||||
|
DHCPIpUdpEncoder_Init(&o->send_encoder, PacketCopier_GetOutput(&o->send_copier), BReactor_PendingGroup(o->reactor));
|
||||||
|
|
||||||
|
// init buffer
|
||||||
|
if (!SinglePacketBuffer_Init(&o->send_buffer, DHCPIpUdpEncoder_GetOutput(&o->send_encoder), BDatagram_SendAsync_GetIf(&o->dgram), BReactor_PendingGroup(o->reactor))) {
|
||||||
|
BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed");
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init receiving
|
||||||
|
|
||||||
|
// init copier
|
||||||
|
PacketCopier_Init(&o->recv_copier, dhcp_mtu, BReactor_PendingGroup(o->reactor));
|
||||||
|
|
||||||
|
// init decoder
|
||||||
|
DHCPIpUdpDecoder_Init(&o->recv_decoder, PacketCopier_GetInput(&o->recv_copier), BReactor_PendingGroup(o->reactor));
|
||||||
|
|
||||||
|
// init buffer
|
||||||
|
if (!SinglePacketBuffer_Init(&o->recv_buffer, BDatagram_RecvAsync_GetIf(&o->dgram), DHCPIpUdpDecoder_GetInput(&o->recv_decoder), BReactor_PendingGroup(o->reactor))) {
|
||||||
|
BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed");
|
||||||
|
goto fail3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init options
|
||||||
|
struct BDHCPClientCore_opts core_opts;
|
||||||
|
core_opts.hostname = opts.hostname;
|
||||||
|
core_opts.vendorclassid = opts.vendorclassid;
|
||||||
|
core_opts.clientid = opts.clientid;
|
||||||
|
core_opts.clientid_len = opts.clientid_len;
|
||||||
|
|
||||||
|
// auto-generate clientid from MAC if requested
|
||||||
|
uint8_t mac_cid[7];
|
||||||
|
if (opts.auto_clientid) {
|
||||||
|
mac_cid[0] = DHCP_HARDWARE_ADDRESS_TYPE_ETHERNET;
|
||||||
|
memcpy(mac_cid + 1, if_mac, 6);
|
||||||
|
core_opts.clientid = mac_cid;
|
||||||
|
core_opts.clientid_len = sizeof(mac_cid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// init dhcp
|
||||||
|
if (!BDHCPClientCore_Init(&o->dhcp, PacketCopier_GetInput(&o->send_copier), PacketCopier_GetOutput(&o->recv_copier), if_mac, core_opts, o->reactor, random2, o,
|
||||||
|
(BDHCPClientCore_func_getsendermac)dhcp_func_getsendermac,
|
||||||
|
(BDHCPClientCore_handler)dhcp_handler
|
||||||
|
)) {
|
||||||
|
BLog(BLOG_ERROR, "BDHCPClientCore_Init failed");
|
||||||
|
goto fail4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set not up
|
||||||
|
o->up = 0;
|
||||||
|
|
||||||
|
DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor));
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail4:
|
||||||
|
SinglePacketBuffer_Free(&o->recv_buffer);
|
||||||
|
fail3:
|
||||||
|
DHCPIpUdpDecoder_Free(&o->recv_decoder);
|
||||||
|
PacketCopier_Free(&o->recv_copier);
|
||||||
|
SinglePacketBuffer_Free(&o->send_buffer);
|
||||||
|
fail2:
|
||||||
|
DHCPIpUdpEncoder_Free(&o->send_encoder);
|
||||||
|
PacketCopier_Free(&o->send_copier);
|
||||||
|
BDatagram_RecvAsync_Free(&o->dgram);
|
||||||
|
BDatagram_SendAsync_Free(&o->dgram);
|
||||||
|
fail1:
|
||||||
|
BDatagram_Free(&o->dgram);
|
||||||
|
fail0:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BDHCPClient_Free (BDHCPClient *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
DebugError_Free(&o->d_err);
|
||||||
|
|
||||||
|
// free dhcp
|
||||||
|
BDHCPClientCore_Free(&o->dhcp);
|
||||||
|
|
||||||
|
// free receiving
|
||||||
|
SinglePacketBuffer_Free(&o->recv_buffer);
|
||||||
|
DHCPIpUdpDecoder_Free(&o->recv_decoder);
|
||||||
|
PacketCopier_Free(&o->recv_copier);
|
||||||
|
|
||||||
|
// free sending
|
||||||
|
SinglePacketBuffer_Free(&o->send_buffer);
|
||||||
|
DHCPIpUdpEncoder_Free(&o->send_encoder);
|
||||||
|
PacketCopier_Free(&o->send_copier);
|
||||||
|
|
||||||
|
// free dgram interfaces
|
||||||
|
BDatagram_RecvAsync_Free(&o->dgram);
|
||||||
|
BDatagram_SendAsync_Free(&o->dgram);
|
||||||
|
|
||||||
|
// free dgram
|
||||||
|
BDatagram_Free(&o->dgram);
|
||||||
|
}
|
||||||
|
|
||||||
|
int BDHCPClient_IsUp (BDHCPClient *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
return o->up;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BDHCPClient_GetClientIP (BDHCPClient *o, uint32_t *out_ip)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(o->up)
|
||||||
|
|
||||||
|
BDHCPClientCore_GetClientIP(&o->dhcp, out_ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BDHCPClient_GetClientMask (BDHCPClient *o, uint32_t *out_mask)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(o->up)
|
||||||
|
|
||||||
|
BDHCPClientCore_GetClientMask(&o->dhcp, out_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
int BDHCPClient_GetRouter (BDHCPClient *o, uint32_t *out_router)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(o->up)
|
||||||
|
|
||||||
|
return BDHCPClientCore_GetRouter(&o->dhcp, out_router);
|
||||||
|
}
|
||||||
|
|
||||||
|
int BDHCPClient_GetDNS (BDHCPClient *o, uint32_t *out_dns_servers, size_t max_dns_servers)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(o->up)
|
||||||
|
|
||||||
|
return BDHCPClientCore_GetDNS(&o->dhcp, out_dns_servers, max_dns_servers);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BDHCPClient_GetServerMAC (BDHCPClient *o, uint8_t *out_mac)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(o->up)
|
||||||
|
|
||||||
|
BDHCPClientCore_GetServerMAC(&o->dhcp, out_mac);
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
/**
|
||||||
|
* @file BDHCPClient.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @section DESCRIPTION
|
||||||
|
*
|
||||||
|
* DHCP client.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_DHCPCLIENT_BDHCPCLIENT_H
|
||||||
|
#define BADVPN_DHCPCLIENT_BDHCPCLIENT_H
|
||||||
|
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <system/BDatagram.h>
|
||||||
|
#include <flow/PacketCopier.h>
|
||||||
|
#include <flow/SinglePacketBuffer.h>
|
||||||
|
#include <dhcpclient/BDHCPClientCore.h>
|
||||||
|
#include <dhcpclient/DHCPIpUdpDecoder.h>
|
||||||
|
#include <dhcpclient/DHCPIpUdpEncoder.h>
|
||||||
|
|
||||||
|
#define BDHCPCLIENT_EVENT_UP 1
|
||||||
|
#define BDHCPCLIENT_EVENT_DOWN 2
|
||||||
|
#define BDHCPCLIENT_EVENT_ERROR 3
|
||||||
|
|
||||||
|
#define BDHCPCLIENT_MAX_DOMAIN_NAME_SERVERS BDHCPCLIENTCORE_MAX_DOMAIN_NAME_SERVERS
|
||||||
|
|
||||||
|
typedef void (*BDHCPClient_handler) (void *user, int event);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
BReactor *reactor;
|
||||||
|
BDatagram dgram;
|
||||||
|
BDHCPClient_handler handler;
|
||||||
|
void *user;
|
||||||
|
PacketCopier send_copier;
|
||||||
|
DHCPIpUdpEncoder send_encoder;
|
||||||
|
SinglePacketBuffer send_buffer;
|
||||||
|
SinglePacketBuffer recv_buffer;
|
||||||
|
DHCPIpUdpDecoder recv_decoder;
|
||||||
|
PacketCopier recv_copier;
|
||||||
|
BDHCPClientCore dhcp;
|
||||||
|
int up;
|
||||||
|
DebugError d_err;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} BDHCPClient;
|
||||||
|
|
||||||
|
struct BDHCPClient_opts {
|
||||||
|
const char *hostname;
|
||||||
|
const char *vendorclassid;
|
||||||
|
const uint8_t *clientid;
|
||||||
|
size_t clientid_len;
|
||||||
|
int auto_clientid;
|
||||||
|
};
|
||||||
|
|
||||||
|
int BDHCPClient_Init (BDHCPClient *o, const char *ifname, struct BDHCPClient_opts opts, BReactor *reactor, BRandom2 *random2, BDHCPClient_handler handler, void *user);
|
||||||
|
void BDHCPClient_Free (BDHCPClient *o);
|
||||||
|
int BDHCPClient_IsUp (BDHCPClient *o);
|
||||||
|
void BDHCPClient_GetClientIP (BDHCPClient *o, uint32_t *out_ip);
|
||||||
|
void BDHCPClient_GetClientMask (BDHCPClient *o, uint32_t *out_mask);
|
||||||
|
int BDHCPClient_GetRouter (BDHCPClient *o, uint32_t *out_router);
|
||||||
|
int BDHCPClient_GetDNS (BDHCPClient *o, uint32_t *out_dns_servers, size_t max_dns_servers);
|
||||||
|
void BDHCPClient_GetServerMAC (BDHCPClient *o, uint8_t *out_mac);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,860 @@
|
||||||
|
/**
|
||||||
|
* @file BDHCPClientCore.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <misc/byteorder.h>
|
||||||
|
#include <misc/minmax.h>
|
||||||
|
#include <misc/balloc.h>
|
||||||
|
#include <misc/bsize.h>
|
||||||
|
#include <misc/dhcp_proto.h>
|
||||||
|
#include <base/BLog.h>
|
||||||
|
|
||||||
|
#include <dhcpclient/BDHCPClientCore.h>
|
||||||
|
|
||||||
|
#include <generated/blog_channel_BDHCPClientCore.h>
|
||||||
|
|
||||||
|
#define RESET_TIMEOUT 4000
|
||||||
|
#define REQUEST_TIMEOUT 3000
|
||||||
|
#define RENEW_REQUEST_TIMEOUT 20000
|
||||||
|
#define MAX_REQUESTS 4
|
||||||
|
#define RENEW_TIMEOUT(lease) ((btime_t)500 * (lease))
|
||||||
|
#define XID_REUSE_MAX 8
|
||||||
|
|
||||||
|
#define LEASE_TIMEOUT(lease) ((btime_t)1000 * (lease) - RENEW_TIMEOUT(lease))
|
||||||
|
|
||||||
|
#define STATE_RESETTING 1
|
||||||
|
#define STATE_SENT_DISCOVER 2
|
||||||
|
#define STATE_SENT_REQUEST 3
|
||||||
|
#define STATE_FINISHED 4
|
||||||
|
#define STATE_RENEWING 5
|
||||||
|
|
||||||
|
#define IP_UDP_HEADERS_SIZE 28
|
||||||
|
|
||||||
|
static void report_up (BDHCPClientCore *o)
|
||||||
|
{
|
||||||
|
o->handler(o->user, BDHCPCLIENTCORE_EVENT_UP);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void report_down (BDHCPClientCore *o)
|
||||||
|
{
|
||||||
|
o->handler(o->user, BDHCPCLIENTCORE_EVENT_DOWN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_message (
|
||||||
|
BDHCPClientCore *o,
|
||||||
|
int type,
|
||||||
|
uint32_t xid,
|
||||||
|
int have_requested_ip_address, uint32_t requested_ip_address,
|
||||||
|
int have_dhcp_server_identifier, uint32_t dhcp_server_identifier
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ASSERT(type == DHCP_MESSAGE_TYPE_DISCOVER || type == DHCP_MESSAGE_TYPE_REQUEST)
|
||||||
|
|
||||||
|
if (o->sending) {
|
||||||
|
BLog(BLOG_ERROR, "already sending");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write header
|
||||||
|
struct dhcp_header header;
|
||||||
|
memset(&header, 0, sizeof(header));
|
||||||
|
header.op = hton8(DHCP_OP_BOOTREQUEST);
|
||||||
|
header.htype = hton8(DHCP_HARDWARE_ADDRESS_TYPE_ETHERNET);
|
||||||
|
header.hlen = hton8(6);
|
||||||
|
header.xid = xid;
|
||||||
|
header.secs = hton16(0);
|
||||||
|
memcpy(header.chaddr, o->client_mac_addr, sizeof(o->client_mac_addr));
|
||||||
|
header.magic = hton32(DHCP_MAGIC);
|
||||||
|
memcpy(o->send_buf, &header, sizeof(header));
|
||||||
|
|
||||||
|
// write options
|
||||||
|
|
||||||
|
char *out = o->send_buf + sizeof(header);
|
||||||
|
struct dhcp_option_header oh;
|
||||||
|
|
||||||
|
// DHCP message type
|
||||||
|
{
|
||||||
|
oh.type = hton8(DHCP_OPTION_DHCP_MESSAGE_TYPE);
|
||||||
|
oh.len = hton8(sizeof(struct dhcp_option_dhcp_message_type));
|
||||||
|
struct dhcp_option_dhcp_message_type opt;
|
||||||
|
opt.type = hton8(type);
|
||||||
|
memcpy(out, &oh, sizeof(oh));
|
||||||
|
memcpy(out + sizeof(oh), &opt, sizeof(opt));
|
||||||
|
out += sizeof(oh) + sizeof(opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (have_requested_ip_address) {
|
||||||
|
// requested IP address
|
||||||
|
oh.type = hton8(DHCP_OPTION_REQUESTED_IP_ADDRESS);
|
||||||
|
oh.len = hton8(sizeof(struct dhcp_option_addr));
|
||||||
|
struct dhcp_option_addr opt;
|
||||||
|
opt.addr = requested_ip_address;
|
||||||
|
memcpy(out, &oh, sizeof(oh));
|
||||||
|
memcpy(out + sizeof(oh), &opt, sizeof(opt));
|
||||||
|
out += sizeof(oh) + sizeof(opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (have_dhcp_server_identifier) {
|
||||||
|
// DHCP server identifier
|
||||||
|
oh.type = hton8(DHCP_OPTION_DHCP_SERVER_IDENTIFIER);
|
||||||
|
oh.len = hton8(sizeof(struct dhcp_option_dhcp_server_identifier));
|
||||||
|
struct dhcp_option_dhcp_server_identifier opt;
|
||||||
|
opt.id = dhcp_server_identifier;
|
||||||
|
memcpy(out, &oh, sizeof(oh));
|
||||||
|
memcpy(out + sizeof(oh), &opt, sizeof(opt));
|
||||||
|
out += sizeof(oh) + sizeof(opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// maximum message size
|
||||||
|
{
|
||||||
|
oh.type = hton8(DHCP_OPTION_MAXIMUM_MESSAGE_SIZE);
|
||||||
|
oh.len = hton8(sizeof(struct dhcp_option_maximum_message_size));
|
||||||
|
struct dhcp_option_maximum_message_size opt;
|
||||||
|
opt.size = hton16(IP_UDP_HEADERS_SIZE + PacketRecvInterface_GetMTU(o->recv_if));
|
||||||
|
memcpy(out, &oh, sizeof(oh));
|
||||||
|
memcpy(out + sizeof(oh), &opt, sizeof(opt));
|
||||||
|
out += sizeof(oh) + sizeof(opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// parameter request list
|
||||||
|
{
|
||||||
|
oh.type = hton8(DHCP_OPTION_PARAMETER_REQUEST_LIST);
|
||||||
|
oh.len = hton8(4);
|
||||||
|
uint8_t opt[4];
|
||||||
|
opt[0] = DHCP_OPTION_SUBNET_MASK;
|
||||||
|
opt[1] = DHCP_OPTION_ROUTER;
|
||||||
|
opt[2] = DHCP_OPTION_DOMAIN_NAME_SERVER;
|
||||||
|
opt[3] = DHCP_OPTION_IP_ADDRESS_LEASE_TIME;
|
||||||
|
memcpy(out, &oh, sizeof(oh));
|
||||||
|
memcpy(out + sizeof(oh), &opt, sizeof(opt));
|
||||||
|
out += sizeof(oh) + sizeof(opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o->hostname) {
|
||||||
|
// host name
|
||||||
|
oh.type = hton8(DHCP_OPTION_HOST_NAME);
|
||||||
|
oh.len = hton8(strlen(o->hostname));
|
||||||
|
memcpy(out, &oh, sizeof(oh));
|
||||||
|
memcpy(out + sizeof(oh), o->hostname, strlen(o->hostname));
|
||||||
|
out += sizeof(oh) + strlen(o->hostname);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o->vendorclassid) {
|
||||||
|
// vendor class identifier
|
||||||
|
oh.type = hton8(DHCP_OPTION_VENDOR_CLASS_IDENTIFIER);
|
||||||
|
oh.len = hton8(strlen(o->vendorclassid));
|
||||||
|
memcpy(out, &oh, sizeof(oh));
|
||||||
|
memcpy(out + sizeof(oh), o->vendorclassid, strlen(o->vendorclassid));
|
||||||
|
out += sizeof(oh) + strlen(o->vendorclassid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o->clientid) {
|
||||||
|
// client identifier
|
||||||
|
oh.type = hton8(DHCP_OPTION_CLIENT_IDENTIFIER);
|
||||||
|
oh.len = hton8(o->clientid_len);
|
||||||
|
memcpy(out, &oh, sizeof(oh));
|
||||||
|
memcpy(out + sizeof(oh), o->clientid, o->clientid_len);
|
||||||
|
out += sizeof(oh) + o->clientid_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// end option
|
||||||
|
uint8_t end = 0xFF;
|
||||||
|
memcpy(out, &end, sizeof(end));
|
||||||
|
out += sizeof(end);
|
||||||
|
|
||||||
|
// send it
|
||||||
|
PacketPassInterface_Sender_Send(o->send_if, (uint8_t *)o->send_buf, out - o->send_buf);
|
||||||
|
o->sending = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_handler_done (BDHCPClientCore *o)
|
||||||
|
{
|
||||||
|
ASSERT(o->sending)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
o->sending = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void recv_handler_done (BDHCPClientCore *o, int data_len)
|
||||||
|
{
|
||||||
|
ASSERT(data_len >= 0)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// receive more packets
|
||||||
|
PacketRecvInterface_Receiver_Recv(o->recv_if, (uint8_t *)o->recv_buf);
|
||||||
|
|
||||||
|
if (o->state == STATE_RESETTING) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check header
|
||||||
|
|
||||||
|
if (data_len < sizeof(struct dhcp_header)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dhcp_header header;
|
||||||
|
memcpy(&header, o->recv_buf, sizeof(header));
|
||||||
|
|
||||||
|
if (ntoh8(header.op) != DHCP_OP_BOOTREPLY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ntoh8(header.htype) != DHCP_HARDWARE_ADDRESS_TYPE_ETHERNET) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ntoh8(header.hlen) != 6) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.xid != o->xid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(header.chaddr, o->client_mac_addr, sizeof(o->client_mac_addr))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ntoh32(header.magic) != DHCP_MAGIC) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse and check options
|
||||||
|
|
||||||
|
uint8_t *pos = (uint8_t *)o->recv_buf + sizeof(header);
|
||||||
|
int len = data_len - sizeof(header);
|
||||||
|
|
||||||
|
int have_end = 0;
|
||||||
|
|
||||||
|
int dhcp_message_type = -1;
|
||||||
|
|
||||||
|
int have_dhcp_server_identifier = 0;
|
||||||
|
uint32_t dhcp_server_identifier = 0; // to remove warning
|
||||||
|
|
||||||
|
int have_ip_address_lease_time = 0;
|
||||||
|
uint32_t ip_address_lease_time = 0; // to remove warning
|
||||||
|
|
||||||
|
int have_subnet_mask = 0;
|
||||||
|
uint32_t subnet_mask = 0; // to remove warning
|
||||||
|
|
||||||
|
int have_router = 0;
|
||||||
|
uint32_t router = 0; // to remove warning
|
||||||
|
|
||||||
|
int domain_name_servers_count = 0;
|
||||||
|
uint32_t domain_name_servers[BDHCPCLIENTCORE_MAX_DOMAIN_NAME_SERVERS];
|
||||||
|
|
||||||
|
while (len > 0) {
|
||||||
|
// padding option ?
|
||||||
|
if (*pos == 0) {
|
||||||
|
pos++;
|
||||||
|
len--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (have_end) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// end option ?
|
||||||
|
if (*pos == 0xff) {
|
||||||
|
pos++;
|
||||||
|
len--;
|
||||||
|
have_end = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check option header
|
||||||
|
if (len < sizeof(struct dhcp_option_header)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct dhcp_option_header opt;
|
||||||
|
memcpy(&opt, pos, sizeof(opt));
|
||||||
|
pos += sizeof(opt);
|
||||||
|
len -= sizeof(opt);
|
||||||
|
int opt_type = ntoh8(opt.type);
|
||||||
|
int opt_len = ntoh8(opt.len);
|
||||||
|
|
||||||
|
// check option payload
|
||||||
|
if (opt_len > len) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t *optval = pos;
|
||||||
|
pos += opt_len;
|
||||||
|
len -= opt_len;
|
||||||
|
|
||||||
|
switch (opt_type) {
|
||||||
|
case DHCP_OPTION_DHCP_MESSAGE_TYPE: {
|
||||||
|
if (opt_len != sizeof(struct dhcp_option_dhcp_message_type)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct dhcp_option_dhcp_message_type val;
|
||||||
|
memcpy(&val, optval, sizeof(val));
|
||||||
|
|
||||||
|
dhcp_message_type = ntoh8(val.type);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DHCP_OPTION_DHCP_SERVER_IDENTIFIER: {
|
||||||
|
if (opt_len != sizeof(struct dhcp_option_dhcp_server_identifier)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct dhcp_option_dhcp_server_identifier val;
|
||||||
|
memcpy(&val, optval, sizeof(val));
|
||||||
|
|
||||||
|
dhcp_server_identifier = val.id;
|
||||||
|
have_dhcp_server_identifier = 1;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DHCP_OPTION_IP_ADDRESS_LEASE_TIME: {
|
||||||
|
if (opt_len != sizeof(struct dhcp_option_time)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct dhcp_option_time val;
|
||||||
|
memcpy(&val, optval, sizeof(val));
|
||||||
|
|
||||||
|
ip_address_lease_time = ntoh32(val.time);
|
||||||
|
have_ip_address_lease_time = 1;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DHCP_OPTION_SUBNET_MASK: {
|
||||||
|
if (opt_len != sizeof(struct dhcp_option_addr)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct dhcp_option_addr val;
|
||||||
|
memcpy(&val, optval, sizeof(val));
|
||||||
|
|
||||||
|
subnet_mask = val.addr;
|
||||||
|
have_subnet_mask = 1;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DHCP_OPTION_ROUTER: {
|
||||||
|
if (opt_len != sizeof(struct dhcp_option_addr)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct dhcp_option_addr val;
|
||||||
|
memcpy(&val, optval, sizeof(val));
|
||||||
|
|
||||||
|
router = val.addr;
|
||||||
|
have_router = 1;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DHCP_OPTION_DOMAIN_NAME_SERVER: {
|
||||||
|
if (opt_len % sizeof(struct dhcp_option_addr)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int num_servers = opt_len / sizeof(struct dhcp_option_addr);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < num_servers && i < BDHCPCLIENTCORE_MAX_DOMAIN_NAME_SERVERS; i++) {
|
||||||
|
struct dhcp_option_addr addr;
|
||||||
|
memcpy(&addr, optval + i * sizeof(addr), sizeof(addr));
|
||||||
|
domain_name_servers[i] = addr.addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
domain_name_servers_count = i;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!have_end) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dhcp_message_type == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dhcp_message_type != DHCP_MESSAGE_TYPE_OFFER && dhcp_message_type != DHCP_MESSAGE_TYPE_ACK && dhcp_message_type != DHCP_MESSAGE_TYPE_NAK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!have_dhcp_server_identifier) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dhcp_message_type == DHCP_MESSAGE_TYPE_NAK) {
|
||||||
|
if (o->state != STATE_SENT_REQUEST && o->state != STATE_FINISHED && o->state != STATE_RENEWING) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dhcp_server_identifier != o->offered.dhcp_server_identifier) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o->state == STATE_SENT_REQUEST) {
|
||||||
|
BLog(BLOG_INFO, "received NAK (in sent request)");
|
||||||
|
|
||||||
|
// stop request timer
|
||||||
|
BReactor_RemoveTimer(o->reactor, &o->request_timer);
|
||||||
|
|
||||||
|
// start reset timer
|
||||||
|
BReactor_SetTimer(o->reactor, &o->reset_timer);
|
||||||
|
|
||||||
|
// set state
|
||||||
|
o->state = STATE_RESETTING;
|
||||||
|
}
|
||||||
|
else if (o->state == STATE_FINISHED) {
|
||||||
|
BLog(BLOG_INFO, "received NAK (in finished)");
|
||||||
|
|
||||||
|
// stop renew timer
|
||||||
|
BReactor_RemoveTimer(o->reactor, &o->renew_timer);
|
||||||
|
|
||||||
|
// start reset timer
|
||||||
|
BReactor_SetTimer(o->reactor, &o->reset_timer);
|
||||||
|
|
||||||
|
// set state
|
||||||
|
o->state = STATE_RESETTING;
|
||||||
|
|
||||||
|
// report to user
|
||||||
|
report_down(o);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else { // STATE_RENEWING
|
||||||
|
BLog(BLOG_INFO, "received NAK (in renewing)");
|
||||||
|
|
||||||
|
// stop renew request timer
|
||||||
|
BReactor_RemoveTimer(o->reactor, &o->renew_request_timer);
|
||||||
|
|
||||||
|
// stop lease timer
|
||||||
|
BReactor_RemoveTimer(o->reactor, &o->lease_timer);
|
||||||
|
|
||||||
|
// start reset timer
|
||||||
|
BReactor_SetTimer(o->reactor, &o->reset_timer);
|
||||||
|
|
||||||
|
// set state
|
||||||
|
o->state = STATE_RESETTING;
|
||||||
|
|
||||||
|
// report to user
|
||||||
|
report_down(o);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ntoh32(header.yiaddr) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!have_ip_address_lease_time) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!have_subnet_mask) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o->state == STATE_SENT_DISCOVER && dhcp_message_type == DHCP_MESSAGE_TYPE_OFFER) {
|
||||||
|
BLog(BLOG_INFO, "received OFFER");
|
||||||
|
|
||||||
|
// remember offer
|
||||||
|
o->offered.yiaddr = header.yiaddr;
|
||||||
|
o->offered.dhcp_server_identifier = dhcp_server_identifier;
|
||||||
|
|
||||||
|
// send request
|
||||||
|
send_message(o, DHCP_MESSAGE_TYPE_REQUEST, o->xid, 1, o->offered.yiaddr, 1, o->offered.dhcp_server_identifier);
|
||||||
|
|
||||||
|
// stop reset timer
|
||||||
|
BReactor_RemoveTimer(o->reactor, &o->reset_timer);
|
||||||
|
|
||||||
|
// start request timer
|
||||||
|
BReactor_SetTimer(o->reactor, &o->request_timer);
|
||||||
|
|
||||||
|
// set state
|
||||||
|
o->state = STATE_SENT_REQUEST;
|
||||||
|
|
||||||
|
// set request count
|
||||||
|
o->request_count = 1;
|
||||||
|
}
|
||||||
|
else if (o->state == STATE_SENT_REQUEST && dhcp_message_type == DHCP_MESSAGE_TYPE_ACK) {
|
||||||
|
if (header.yiaddr != o->offered.yiaddr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dhcp_server_identifier != o->offered.dhcp_server_identifier) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BLog(BLOG_INFO, "received ACK (in sent request)");
|
||||||
|
|
||||||
|
// remember stuff
|
||||||
|
o->acked.ip_address_lease_time = ip_address_lease_time;
|
||||||
|
o->acked.subnet_mask = subnet_mask;
|
||||||
|
o->acked.have_router = have_router;
|
||||||
|
if (have_router) {
|
||||||
|
o->acked.router = router;
|
||||||
|
}
|
||||||
|
o->acked.domain_name_servers_count = domain_name_servers_count;
|
||||||
|
memcpy(o->acked.domain_name_servers, domain_name_servers, domain_name_servers_count * sizeof(uint32_t));
|
||||||
|
o->func_getsendermac(o->user, o->acked.server_mac);
|
||||||
|
|
||||||
|
// stop request timer
|
||||||
|
BReactor_RemoveTimer(o->reactor, &o->request_timer);
|
||||||
|
|
||||||
|
// start renew timer
|
||||||
|
BReactor_SetTimerAfter(o->reactor, &o->renew_timer, RENEW_TIMEOUT(o->acked.ip_address_lease_time));
|
||||||
|
|
||||||
|
// set state
|
||||||
|
o->state = STATE_FINISHED;
|
||||||
|
|
||||||
|
// report to user
|
||||||
|
report_up(o);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (o->state == STATE_RENEWING && dhcp_message_type == DHCP_MESSAGE_TYPE_ACK) {
|
||||||
|
if (header.yiaddr != o->offered.yiaddr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dhcp_server_identifier != o->offered.dhcp_server_identifier) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check parameters?
|
||||||
|
|
||||||
|
BLog(BLOG_INFO, "received ACK (in renewing)");
|
||||||
|
|
||||||
|
// remember stuff
|
||||||
|
o->acked.ip_address_lease_time = ip_address_lease_time;
|
||||||
|
|
||||||
|
// stop renew request timer
|
||||||
|
BReactor_RemoveTimer(o->reactor, &o->renew_request_timer);
|
||||||
|
|
||||||
|
// stop lease timer
|
||||||
|
BReactor_RemoveTimer(o->reactor, &o->lease_timer);
|
||||||
|
|
||||||
|
// start renew timer
|
||||||
|
BReactor_SetTimerAfter(o->reactor, &o->renew_timer, RENEW_TIMEOUT(o->acked.ip_address_lease_time));
|
||||||
|
|
||||||
|
// set state
|
||||||
|
o->state = STATE_FINISHED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void start_process (BDHCPClientCore *o, int force_new_xid)
|
||||||
|
{
|
||||||
|
if (force_new_xid || o->xid_reuse_counter == XID_REUSE_MAX) {
|
||||||
|
// generate xid
|
||||||
|
if (!BRandom2_GenBytes(o->random2, &o->xid, sizeof(o->xid))) {
|
||||||
|
BLog(BLOG_ERROR, "BRandom2_GenBytes failed");
|
||||||
|
o->xid = UINT32_C(3416960072);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset counter
|
||||||
|
o->xid_reuse_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// increment counter
|
||||||
|
o->xid_reuse_counter++;
|
||||||
|
|
||||||
|
// send discover
|
||||||
|
send_message(o, DHCP_MESSAGE_TYPE_DISCOVER, o->xid, 0, 0, 0, 0);
|
||||||
|
|
||||||
|
// set timer
|
||||||
|
BReactor_SetTimer(o->reactor, &o->reset_timer);
|
||||||
|
|
||||||
|
// set state
|
||||||
|
o->state = STATE_SENT_DISCOVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reset_timer_handler (BDHCPClientCore *o)
|
||||||
|
{
|
||||||
|
ASSERT(o->state == STATE_RESETTING || o->state == STATE_SENT_DISCOVER)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
BLog(BLOG_INFO, "reset timer");
|
||||||
|
|
||||||
|
start_process(o, (o->state == STATE_RESETTING));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void request_timer_handler (BDHCPClientCore *o)
|
||||||
|
{
|
||||||
|
ASSERT(o->state == STATE_SENT_REQUEST)
|
||||||
|
ASSERT(o->request_count >= 1)
|
||||||
|
ASSERT(o->request_count <= MAX_REQUESTS)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// if we have sent enough requests, start again
|
||||||
|
if (o->request_count == MAX_REQUESTS) {
|
||||||
|
BLog(BLOG_INFO, "request timer, aborting");
|
||||||
|
|
||||||
|
start_process(o, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BLog(BLOG_INFO, "request timer, retrying");
|
||||||
|
|
||||||
|
// send request
|
||||||
|
send_message(o, DHCP_MESSAGE_TYPE_REQUEST, o->xid, 1, o->offered.yiaddr, 1, o->offered.dhcp_server_identifier);
|
||||||
|
|
||||||
|
// start request timer
|
||||||
|
BReactor_SetTimer(o->reactor, &o->request_timer);
|
||||||
|
|
||||||
|
// increment request count
|
||||||
|
o->request_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void renew_timer_handler (BDHCPClientCore *o)
|
||||||
|
{
|
||||||
|
ASSERT(o->state == STATE_FINISHED)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
BLog(BLOG_INFO, "renew timer");
|
||||||
|
|
||||||
|
// send request
|
||||||
|
send_message(o, DHCP_MESSAGE_TYPE_REQUEST, o->xid, 1, o->offered.yiaddr, 0, 0);
|
||||||
|
|
||||||
|
// start renew request timer
|
||||||
|
BReactor_SetTimer(o->reactor, &o->renew_request_timer);
|
||||||
|
|
||||||
|
// start lease timer
|
||||||
|
BReactor_SetTimerAfter(o->reactor, &o->lease_timer, LEASE_TIMEOUT(o->acked.ip_address_lease_time));
|
||||||
|
|
||||||
|
// set state
|
||||||
|
o->state = STATE_RENEWING;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void renew_request_timer_handler (BDHCPClientCore *o)
|
||||||
|
{
|
||||||
|
ASSERT(o->state == STATE_RENEWING)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
BLog(BLOG_INFO, "renew request timer");
|
||||||
|
|
||||||
|
// send request
|
||||||
|
send_message(o, DHCP_MESSAGE_TYPE_REQUEST, o->xid, 1, o->offered.yiaddr, 0, 0);
|
||||||
|
|
||||||
|
// start renew request timer
|
||||||
|
BReactor_SetTimer(o->reactor, &o->renew_request_timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lease_timer_handler (BDHCPClientCore *o)
|
||||||
|
{
|
||||||
|
ASSERT(o->state == STATE_RENEWING)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
BLog(BLOG_INFO, "lease timer");
|
||||||
|
|
||||||
|
// stop renew request timer
|
||||||
|
BReactor_RemoveTimer(o->reactor, &o->renew_request_timer);
|
||||||
|
|
||||||
|
// start again now
|
||||||
|
start_process(o, 1);
|
||||||
|
|
||||||
|
// report to user
|
||||||
|
report_down(o);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bsize_t maybe_len (const char *str)
|
||||||
|
{
|
||||||
|
return bsize_fromsize(str ? strlen(str) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int BDHCPClientCore_Init (BDHCPClientCore *o, PacketPassInterface *send_if, PacketRecvInterface *recv_if,
|
||||||
|
uint8_t *client_mac_addr, struct BDHCPClientCore_opts opts, BReactor *reactor,
|
||||||
|
BRandom2 *random2, void *user,
|
||||||
|
BDHCPClientCore_func_getsendermac func_getsendermac,
|
||||||
|
BDHCPClientCore_handler handler)
|
||||||
|
{
|
||||||
|
ASSERT(PacketPassInterface_GetMTU(send_if) == PacketRecvInterface_GetMTU(recv_if))
|
||||||
|
ASSERT(PacketPassInterface_GetMTU(send_if) >= 576 - IP_UDP_HEADERS_SIZE)
|
||||||
|
ASSERT(func_getsendermac)
|
||||||
|
ASSERT(handler)
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
o->send_if = send_if;
|
||||||
|
o->recv_if = recv_if;
|
||||||
|
memcpy(o->client_mac_addr, client_mac_addr, sizeof(o->client_mac_addr));
|
||||||
|
o->reactor = reactor;
|
||||||
|
o->random2 = random2;
|
||||||
|
o->user = user;
|
||||||
|
o->func_getsendermac = func_getsendermac;
|
||||||
|
o->handler = handler;
|
||||||
|
|
||||||
|
o->hostname = NULL;
|
||||||
|
o->vendorclassid = NULL;
|
||||||
|
o->clientid = NULL;
|
||||||
|
o->clientid_len = 0;
|
||||||
|
|
||||||
|
// copy options
|
||||||
|
if (opts.hostname && !(o->hostname = strdup(opts.hostname))) {
|
||||||
|
BLog(BLOG_ERROR, "strdup failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
if (opts.vendorclassid && !(o->vendorclassid = strdup(opts.vendorclassid))) {
|
||||||
|
BLog(BLOG_ERROR, "strdup failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
if (opts.clientid) {
|
||||||
|
if (!(o->clientid = BAlloc(opts.clientid_len))) {
|
||||||
|
BLog(BLOG_ERROR, "BAlloc failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
memcpy(o->clientid, opts.clientid, opts.clientid_len);
|
||||||
|
o->clientid_len = opts.clientid_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure options aren't too long
|
||||||
|
bsize_t opts_size = bsize_add(maybe_len(o->hostname), bsize_add(maybe_len(o->vendorclassid), bsize_fromsize(o->clientid_len)));
|
||||||
|
if (opts_size.is_overflow || opts_size.value > 100) {
|
||||||
|
BLog(BLOG_ERROR, "options too long together");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
if (o->hostname && strlen(o->hostname) > 255) {
|
||||||
|
BLog(BLOG_ERROR, "hostname too long");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
if (o->vendorclassid && strlen(o->vendorclassid) > 255) {
|
||||||
|
BLog(BLOG_ERROR, "vendorclassid too long");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
if (o->clientid && o->clientid_len > 255) {
|
||||||
|
BLog(BLOG_ERROR, "clientid too long");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate buffers
|
||||||
|
if (!(o->send_buf = BAlloc(PacketPassInterface_GetMTU(send_if)))) {
|
||||||
|
BLog(BLOG_ERROR, "BAlloc send buf failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
if (!(o->recv_buf = BAlloc(PacketRecvInterface_GetMTU(recv_if)))) {
|
||||||
|
BLog(BLOG_ERROR, "BAlloc recv buf failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init send interface
|
||||||
|
PacketPassInterface_Sender_Init(o->send_if, (PacketPassInterface_handler_done)send_handler_done, o);
|
||||||
|
|
||||||
|
// init receive interface
|
||||||
|
PacketRecvInterface_Receiver_Init(o->recv_if, (PacketRecvInterface_handler_done)recv_handler_done, o);
|
||||||
|
|
||||||
|
// set not sending
|
||||||
|
o->sending = 0;
|
||||||
|
|
||||||
|
// init timers
|
||||||
|
BTimer_Init(&o->reset_timer, RESET_TIMEOUT, (BTimer_handler)reset_timer_handler, o);
|
||||||
|
BTimer_Init(&o->request_timer, REQUEST_TIMEOUT, (BTimer_handler)request_timer_handler, o);
|
||||||
|
BTimer_Init(&o->renew_timer, 0, (BTimer_handler)renew_timer_handler, o);
|
||||||
|
BTimer_Init(&o->renew_request_timer, RENEW_REQUEST_TIMEOUT, (BTimer_handler)renew_request_timer_handler, o);
|
||||||
|
BTimer_Init(&o->lease_timer, 0, (BTimer_handler)lease_timer_handler, o);
|
||||||
|
|
||||||
|
// start receving
|
||||||
|
PacketRecvInterface_Receiver_Recv(o->recv_if, (uint8_t *)o->recv_buf);
|
||||||
|
|
||||||
|
// start
|
||||||
|
start_process(o, 1);
|
||||||
|
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail1:
|
||||||
|
BFree(o->send_buf);
|
||||||
|
fail0:
|
||||||
|
BFree(o->clientid);
|
||||||
|
free(o->vendorclassid);
|
||||||
|
free(o->hostname);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BDHCPClientCore_Free (BDHCPClientCore *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
|
||||||
|
// free timers
|
||||||
|
BReactor_RemoveTimer(o->reactor, &o->lease_timer);
|
||||||
|
BReactor_RemoveTimer(o->reactor, &o->renew_request_timer);
|
||||||
|
BReactor_RemoveTimer(o->reactor, &o->renew_timer);
|
||||||
|
BReactor_RemoveTimer(o->reactor, &o->request_timer);
|
||||||
|
BReactor_RemoveTimer(o->reactor, &o->reset_timer);
|
||||||
|
|
||||||
|
// free buffers
|
||||||
|
BFree(o->recv_buf);
|
||||||
|
BFree(o->send_buf);
|
||||||
|
|
||||||
|
// free options
|
||||||
|
BFree(o->clientid);
|
||||||
|
free(o->vendorclassid);
|
||||||
|
free(o->hostname);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BDHCPClientCore_GetClientIP (BDHCPClientCore *o, uint32_t *out_ip)
|
||||||
|
{
|
||||||
|
ASSERT(o->state == STATE_FINISHED || o->state == STATE_RENEWING)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
*out_ip = o->offered.yiaddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BDHCPClientCore_GetClientMask (BDHCPClientCore *o, uint32_t *out_mask)
|
||||||
|
{
|
||||||
|
ASSERT(o->state == STATE_FINISHED || o->state == STATE_RENEWING)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
*out_mask = o->acked.subnet_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BDHCPClientCore_GetRouter (BDHCPClientCore *o, uint32_t *out_router)
|
||||||
|
{
|
||||||
|
ASSERT(o->state == STATE_FINISHED || o->state == STATE_RENEWING)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
if (!o->acked.have_router) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_router = o->acked.router;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BDHCPClientCore_GetDNS (BDHCPClientCore *o, uint32_t *out_dns_servers, size_t max_dns_servers)
|
||||||
|
{
|
||||||
|
ASSERT(o->state == STATE_FINISHED || o->state == STATE_RENEWING)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
int num_return = bmin_int(o->acked.domain_name_servers_count, max_dns_servers);
|
||||||
|
|
||||||
|
memcpy(out_dns_servers, o->acked.domain_name_servers, num_return * sizeof(uint32_t));
|
||||||
|
return num_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BDHCPClientCore_GetServerMAC (BDHCPClientCore *o, uint8_t *out_mac)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
ASSERT(o->state == STATE_FINISHED || o->state == STATE_RENEWING)
|
||||||
|
|
||||||
|
memcpy(out_mac, o->acked.server_mac, 6);
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
/**
|
||||||
|
* @file BDHCPClientCore.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @section DESCRIPTION
|
||||||
|
*
|
||||||
|
* DHCP client, excluding system-dependent details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_DHCPCLIENT_BDHCPCLIENTCORE_H
|
||||||
|
#define BADVPN_DHCPCLIENT_BDHCPCLIENTCORE_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <system/BReactor.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <random/BRandom2.h>
|
||||||
|
#include <flow/PacketPassInterface.h>
|
||||||
|
#include <flow/PacketRecvInterface.h>
|
||||||
|
|
||||||
|
#define BDHCPCLIENTCORE_EVENT_UP 1
|
||||||
|
#define BDHCPCLIENTCORE_EVENT_DOWN 2
|
||||||
|
|
||||||
|
#define BDHCPCLIENTCORE_MAX_DOMAIN_NAME_SERVERS 16
|
||||||
|
|
||||||
|
typedef void (*BDHCPClientCore_func_getsendermac) (void *user, uint8_t *out_mac);
|
||||||
|
typedef void (*BDHCPClientCore_handler) (void *user, int event);
|
||||||
|
|
||||||
|
struct BDHCPClientCore_opts {
|
||||||
|
const char *hostname;
|
||||||
|
const char *vendorclassid;
|
||||||
|
const uint8_t *clientid;
|
||||||
|
size_t clientid_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PacketPassInterface *send_if;
|
||||||
|
PacketRecvInterface *recv_if;
|
||||||
|
uint8_t client_mac_addr[6];
|
||||||
|
BReactor *reactor;
|
||||||
|
BRandom2 *random2;
|
||||||
|
void *user;
|
||||||
|
BDHCPClientCore_func_getsendermac func_getsendermac;
|
||||||
|
BDHCPClientCore_handler handler;
|
||||||
|
char *hostname;
|
||||||
|
char *vendorclassid;
|
||||||
|
uint8_t *clientid;
|
||||||
|
size_t clientid_len;
|
||||||
|
char *send_buf;
|
||||||
|
char *recv_buf;
|
||||||
|
int sending;
|
||||||
|
BTimer reset_timer;
|
||||||
|
BTimer request_timer;
|
||||||
|
BTimer renew_timer;
|
||||||
|
BTimer renew_request_timer;
|
||||||
|
BTimer lease_timer;
|
||||||
|
int state;
|
||||||
|
int request_count;
|
||||||
|
uint32_t xid;
|
||||||
|
int xid_reuse_counter;
|
||||||
|
struct {
|
||||||
|
uint32_t yiaddr;
|
||||||
|
uint32_t dhcp_server_identifier;
|
||||||
|
} offered;
|
||||||
|
struct {
|
||||||
|
uint32_t ip_address_lease_time;
|
||||||
|
uint32_t subnet_mask;
|
||||||
|
int have_router;
|
||||||
|
uint32_t router;
|
||||||
|
int domain_name_servers_count;
|
||||||
|
uint32_t domain_name_servers[BDHCPCLIENTCORE_MAX_DOMAIN_NAME_SERVERS];
|
||||||
|
uint8_t server_mac[6];
|
||||||
|
} acked;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} BDHCPClientCore;
|
||||||
|
|
||||||
|
int BDHCPClientCore_Init (BDHCPClientCore *o, PacketPassInterface *send_if, PacketRecvInterface *recv_if,
|
||||||
|
uint8_t *client_mac_addr, struct BDHCPClientCore_opts opts, BReactor *reactor,
|
||||||
|
BRandom2 *random2, void *user,
|
||||||
|
BDHCPClientCore_func_getsendermac func_getsendermac,
|
||||||
|
BDHCPClientCore_handler handler);
|
||||||
|
void BDHCPClientCore_Free (BDHCPClientCore *o);
|
||||||
|
void BDHCPClientCore_GetClientIP (BDHCPClientCore *o, uint32_t *out_ip);
|
||||||
|
void BDHCPClientCore_GetClientMask (BDHCPClientCore *o, uint32_t *out_mask);
|
||||||
|
int BDHCPClientCore_GetRouter (BDHCPClientCore *o, uint32_t *out_router);
|
||||||
|
int BDHCPClientCore_GetDNS (BDHCPClientCore *o, uint32_t *out_dns_servers, size_t max_dns_servers);
|
||||||
|
void BDHCPClientCore_GetServerMAC (BDHCPClientCore *o, uint8_t *out_mac);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,10 @@
|
||||||
|
badvpn_add_library(dhcpclientcore "system;flow;flowextra;badvpn_random" "" BDHCPClientCore.c)
|
||||||
|
|
||||||
|
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
|
set(DHCPCLIENT_SOURCES
|
||||||
|
BDHCPClient.c
|
||||||
|
DHCPIpUdpEncoder.c
|
||||||
|
DHCPIpUdpDecoder.c
|
||||||
|
)
|
||||||
|
badvpn_add_library(dhcpclient "system;flow;dhcpclientcore" "" "${DHCPCLIENT_SOURCES}")
|
||||||
|
endif ()
|
|
@ -0,0 +1,137 @@
|
||||||
|
/**
|
||||||
|
* @file DHCPIpUdpDecoder.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <misc/ipv4_proto.h>
|
||||||
|
#include <misc/udp_proto.h>
|
||||||
|
#include <misc/byteorder.h>
|
||||||
|
|
||||||
|
#include <dhcpclient/DHCPIpUdpDecoder.h>
|
||||||
|
|
||||||
|
#define DHCP_SERVER_PORT 67
|
||||||
|
#define DHCP_CLIENT_PORT 68
|
||||||
|
|
||||||
|
#define IPUDP_HEADER_SIZE (sizeof(struct ipv4_header) + sizeof(struct udp_header))
|
||||||
|
|
||||||
|
static void input_handler_send (DHCPIpUdpDecoder *o, uint8_t *data, int data_len)
|
||||||
|
{
|
||||||
|
ASSERT(data_len >= 0)
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
struct ipv4_header iph;
|
||||||
|
uint8_t *pl;
|
||||||
|
int pl_len;
|
||||||
|
|
||||||
|
if (!ipv4_check(data, data_len, &iph, &pl, &pl_len)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ntoh8(iph.protocol) != IPV4_PROTOCOL_UDP) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pl_len < sizeof(struct udp_header)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
struct udp_header udph;
|
||||||
|
memcpy(&udph, pl, sizeof(udph));
|
||||||
|
|
||||||
|
if (ntoh16(udph.source_port) != DHCP_SERVER_PORT) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ntoh16(udph.dest_port) != DHCP_CLIENT_PORT) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
int udph_length = ntoh16(udph.length);
|
||||||
|
if (udph_length < sizeof(udph)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (udph_length > data_len - (pl - data)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ntoh16(udph.checksum) != 0) {
|
||||||
|
uint16_t checksum_in_packet = udph.checksum;
|
||||||
|
udph.checksum = 0;
|
||||||
|
uint16_t checksum_computed = udp_checksum(&udph, pl + sizeof(udph), udph_length - sizeof(udph), iph.source_address, iph.destination_address);
|
||||||
|
if (checksum_in_packet != checksum_computed) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pass payload to output
|
||||||
|
PacketPassInterface_Sender_Send(o->output, pl + sizeof(udph), udph_length - sizeof(udph));
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
PacketPassInterface_Done(&o->input);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_handler_done (DHCPIpUdpDecoder *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
PacketPassInterface_Done(&o->input);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DHCPIpUdpDecoder_Init (DHCPIpUdpDecoder *o, PacketPassInterface *output, BPendingGroup *pg)
|
||||||
|
{
|
||||||
|
ASSERT(PacketPassInterface_GetMTU(output) <= INT_MAX - IPUDP_HEADER_SIZE)
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
o->output = output;
|
||||||
|
|
||||||
|
// init output
|
||||||
|
PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o);
|
||||||
|
|
||||||
|
// init input
|
||||||
|
PacketPassInterface_Init(&o->input, IPUDP_HEADER_SIZE + PacketPassInterface_GetMTU(o->output), (PacketPassInterface_handler_send)input_handler_send, o, pg);
|
||||||
|
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DHCPIpUdpDecoder_Free (DHCPIpUdpDecoder *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
|
||||||
|
// free input
|
||||||
|
PacketPassInterface_Free(&o->input);
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketPassInterface * DHCPIpUdpDecoder_GetInput (DHCPIpUdpDecoder *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
return &o->input;
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/**
|
||||||
|
* @file DHCPIpUdpDecoder.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_DHCPCLIENT_DHCPIPUDPDECODER_H
|
||||||
|
#define BADVPN_DHCPCLIENT_DHCPIPUDPDECODER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <flow/PacketPassInterface.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PacketPassInterface *output;
|
||||||
|
PacketPassInterface input;
|
||||||
|
uint8_t *data;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} DHCPIpUdpDecoder;
|
||||||
|
|
||||||
|
void DHCPIpUdpDecoder_Init (DHCPIpUdpDecoder *o, PacketPassInterface *output, BPendingGroup *pg);
|
||||||
|
void DHCPIpUdpDecoder_Free (DHCPIpUdpDecoder *o);
|
||||||
|
PacketPassInterface * DHCPIpUdpDecoder_GetInput (DHCPIpUdpDecoder *o);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,119 @@
|
||||||
|
/**
|
||||||
|
* @file DHCPIpUdpEncoder.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <misc/ipv4_proto.h>
|
||||||
|
#include <misc/udp_proto.h>
|
||||||
|
#include <misc/byteorder.h>
|
||||||
|
|
||||||
|
#include <dhcpclient/DHCPIpUdpEncoder.h>
|
||||||
|
|
||||||
|
#define DHCP_SERVER_PORT 67
|
||||||
|
#define DHCP_CLIENT_PORT 68
|
||||||
|
|
||||||
|
#define IPUDP_HEADER_SIZE (sizeof(struct ipv4_header) + sizeof(struct udp_header))
|
||||||
|
|
||||||
|
static void output_handler_recv (DHCPIpUdpEncoder *o, uint8_t *data)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// remember output packet
|
||||||
|
o->data = data;
|
||||||
|
|
||||||
|
// receive payload
|
||||||
|
PacketRecvInterface_Receiver_Recv(o->input, o->data + IPUDP_HEADER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void input_handler_done (DHCPIpUdpEncoder *o, int data_len)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
// build IP header
|
||||||
|
struct ipv4_header iph;
|
||||||
|
iph.version4_ihl4 = IPV4_MAKE_VERSION_IHL(sizeof(iph));
|
||||||
|
iph.ds = hton8(0);
|
||||||
|
iph.total_length = hton16(IPUDP_HEADER_SIZE + data_len);
|
||||||
|
iph.identification = hton16(0);
|
||||||
|
iph.flags3_fragmentoffset13 = hton16(0);
|
||||||
|
iph.ttl = hton8(64);
|
||||||
|
iph.protocol = hton8(IPV4_PROTOCOL_UDP);
|
||||||
|
iph.checksum = hton16(0);
|
||||||
|
iph.source_address = hton32(0x00000000);
|
||||||
|
iph.destination_address = hton32(0xFFFFFFFF);
|
||||||
|
iph.checksum = ipv4_checksum(&iph, NULL, 0);
|
||||||
|
|
||||||
|
// write UDP header
|
||||||
|
struct udp_header udph;
|
||||||
|
udph.source_port = hton16(DHCP_CLIENT_PORT);
|
||||||
|
udph.dest_port = hton16(DHCP_SERVER_PORT);
|
||||||
|
udph.length = hton16(sizeof(udph) + data_len);
|
||||||
|
udph.checksum = hton16(0);
|
||||||
|
udph.checksum = udp_checksum(&udph, o->data + IPUDP_HEADER_SIZE, data_len, iph.source_address, iph.destination_address);
|
||||||
|
|
||||||
|
// write header
|
||||||
|
memcpy(o->data, &iph, sizeof(iph));
|
||||||
|
memcpy(o->data + sizeof(iph), &udph, sizeof(udph));
|
||||||
|
|
||||||
|
// finish packet
|
||||||
|
PacketRecvInterface_Done(&o->output, IPUDP_HEADER_SIZE + data_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DHCPIpUdpEncoder_Init (DHCPIpUdpEncoder *o, PacketRecvInterface *input, BPendingGroup *pg)
|
||||||
|
{
|
||||||
|
ASSERT(PacketRecvInterface_GetMTU(input) <= INT_MAX - IPUDP_HEADER_SIZE)
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
o->input = input;
|
||||||
|
|
||||||
|
// init input
|
||||||
|
PacketRecvInterface_Receiver_Init(o->input, (PacketRecvInterface_handler_done)input_handler_done, o);
|
||||||
|
|
||||||
|
// init output
|
||||||
|
PacketRecvInterface_Init(&o->output, IPUDP_HEADER_SIZE + PacketRecvInterface_GetMTU(o->input), (PacketRecvInterface_handler_recv)output_handler_recv, o, pg);
|
||||||
|
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DHCPIpUdpEncoder_Free (DHCPIpUdpEncoder *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
|
||||||
|
// free output
|
||||||
|
PacketRecvInterface_Free(&o->output);
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketRecvInterface * DHCPIpUdpEncoder_GetOutput (DHCPIpUdpEncoder *o)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&o->d_obj);
|
||||||
|
|
||||||
|
return &o->output;
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/**
|
||||||
|
* @file DHCPIpUdpEncoder.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_DHCPCLIENT_DHCPIPUDPENCODER_H
|
||||||
|
#define BADVPN_DHCPCLIENT_DHCPIPUDPENCODER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <flow/PacketRecvInterface.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PacketRecvInterface *input;
|
||||||
|
PacketRecvInterface output;
|
||||||
|
uint8_t *data;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} DHCPIpUdpEncoder;
|
||||||
|
|
||||||
|
void DHCPIpUdpEncoder_Init (DHCPIpUdpEncoder *o, PacketRecvInterface *input, BPendingGroup *pg);
|
||||||
|
void DHCPIpUdpEncoder_Free (DHCPIpUdpEncoder *o);
|
||||||
|
PacketRecvInterface * DHCPIpUdpEncoder_GetOutput (DHCPIpUdpEncoder *o);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,10 @@
|
||||||
|
add_executable(dostest-server
|
||||||
|
dostest-server.c
|
||||||
|
StreamBuffer.c
|
||||||
|
)
|
||||||
|
target_link_libraries(dostest-server base system)
|
||||||
|
|
||||||
|
add_executable(dostest-attacker
|
||||||
|
dostest-attacker.c
|
||||||
|
)
|
||||||
|
target_link_libraries(dostest-attacker base system)
|
|
@ -0,0 +1,147 @@
|
||||||
|
/**
|
||||||
|
* @file StreamBuffer.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <misc/balloc.h>
|
||||||
|
#include <misc/minmax.h>
|
||||||
|
|
||||||
|
#include "StreamBuffer.h"
|
||||||
|
|
||||||
|
// called when receive operation is complete
|
||||||
|
static void input_handler_done (void *vo, int data_len)
|
||||||
|
{
|
||||||
|
StreamBuffer *o = (StreamBuffer *)vo;
|
||||||
|
ASSERT(data_len > 0)
|
||||||
|
ASSERT(data_len <= o->buf_size - (o->buf_start + o->buf_used))
|
||||||
|
|
||||||
|
// remember if buffer was empty
|
||||||
|
int was_empty = (o->buf_used == 0);
|
||||||
|
|
||||||
|
// increment buf_used by the amount that was received
|
||||||
|
o->buf_used += data_len;
|
||||||
|
|
||||||
|
// start another receive operation unless buffer is full
|
||||||
|
if (o->buf_used < o->buf_size - o->buf_start) {
|
||||||
|
int end = o->buf_start + o->buf_used;
|
||||||
|
StreamRecvInterface_Receiver_Recv(o->input, o->buf + end, o->buf_size - end);
|
||||||
|
}
|
||||||
|
else if (o->buf_used < o->buf_size) {
|
||||||
|
// wrap around
|
||||||
|
StreamRecvInterface_Receiver_Recv(o->input, o->buf, o->buf_start);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if buffer was empty before, start send operation
|
||||||
|
if (was_empty) {
|
||||||
|
StreamPassInterface_Sender_Send(o->output, o->buf + o->buf_start, o->buf_used);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// called when send operation is complete
|
||||||
|
static void output_handler_done (void *vo, int data_len)
|
||||||
|
{
|
||||||
|
StreamBuffer *o = (StreamBuffer *)vo;
|
||||||
|
ASSERT(data_len > 0)
|
||||||
|
ASSERT(data_len <= o->buf_used)
|
||||||
|
ASSERT(data_len <= o->buf_size - o->buf_start)
|
||||||
|
|
||||||
|
// remember if buffer was full
|
||||||
|
int was_full = (o->buf_used == o->buf_size);
|
||||||
|
|
||||||
|
// increment buf_start and decrement buf_used by the
|
||||||
|
// amount that was sent
|
||||||
|
o->buf_start += data_len;
|
||||||
|
o->buf_used -= data_len;
|
||||||
|
|
||||||
|
// wrap around buf_start
|
||||||
|
if (o->buf_start == o->buf_size) {
|
||||||
|
o->buf_start = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// start receive operation if buffer was full
|
||||||
|
if (was_full) {
|
||||||
|
int end;
|
||||||
|
int avail;
|
||||||
|
if (o->buf_used >= o->buf_size - o->buf_start) {
|
||||||
|
end = o->buf_used - (o->buf_size - o->buf_start);
|
||||||
|
avail = o->buf_start - end;
|
||||||
|
} else {
|
||||||
|
end = o->buf_start + o->buf_used;
|
||||||
|
avail = o->buf_size - end;
|
||||||
|
}
|
||||||
|
StreamRecvInterface_Receiver_Recv(o->input, o->buf + end, avail);
|
||||||
|
}
|
||||||
|
|
||||||
|
// start another receive send unless buffer is empty
|
||||||
|
if (o->buf_used > 0) {
|
||||||
|
int to_send = bmin_int(o->buf_used, o->buf_size - o->buf_start);
|
||||||
|
StreamPassInterface_Sender_Send(o->output, o->buf + o->buf_start, to_send);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int StreamBuffer_Init (StreamBuffer *o, int buf_size, StreamRecvInterface *input, StreamPassInterface *output)
|
||||||
|
{
|
||||||
|
ASSERT(buf_size > 0)
|
||||||
|
ASSERT(input)
|
||||||
|
ASSERT(output)
|
||||||
|
|
||||||
|
// remember arguments
|
||||||
|
o->buf_size = buf_size;
|
||||||
|
o->input = input;
|
||||||
|
o->output = output;
|
||||||
|
|
||||||
|
// allocate buffer memory
|
||||||
|
o->buf = (uint8_t *)BAllocSize(bsize_fromint(o->buf_size));
|
||||||
|
if (!o->buf) {
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set initial buffer state
|
||||||
|
o->buf_start = 0;
|
||||||
|
o->buf_used = 0;
|
||||||
|
|
||||||
|
// set receive and send done callbacks
|
||||||
|
StreamRecvInterface_Receiver_Init(o->input, input_handler_done, o);
|
||||||
|
StreamPassInterface_Sender_Init(o->output, output_handler_done, o);
|
||||||
|
|
||||||
|
// start receive operation
|
||||||
|
StreamRecvInterface_Receiver_Recv(o->input, o->buf, o->buf_size);
|
||||||
|
|
||||||
|
DebugObject_Init(&o->d_obj);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail0:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamBuffer_Free (StreamBuffer *o)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&o->d_obj);
|
||||||
|
|
||||||
|
// free buffer memory
|
||||||
|
BFree(o->buf);
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/**
|
||||||
|
* @file StreamBuffer.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BADVPN_STREAMBUFFER_H
|
||||||
|
#define BADVPN_STREAMBUFFER_H
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <flow/StreamRecvInterface.h>
|
||||||
|
#include <flow/StreamPassInterface.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Buffer object which reads data from a \link StreamRecvInterface and writes
|
||||||
|
* it to a \link StreamPassInterface.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int buf_size;
|
||||||
|
StreamRecvInterface *input;
|
||||||
|
StreamPassInterface *output;
|
||||||
|
uint8_t *buf;
|
||||||
|
int buf_start;
|
||||||
|
int buf_used;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} StreamBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the buffer object.
|
||||||
|
*
|
||||||
|
* @param o object to initialize
|
||||||
|
* @param buf_size size of the buffer. Must be >0.
|
||||||
|
* @param input input interface
|
||||||
|
* @param outout output interface
|
||||||
|
* @return 1 on success, 0 on failure
|
||||||
|
*/
|
||||||
|
int StreamBuffer_Init (StreamBuffer *o, int buf_size, StreamRecvInterface *input, StreamPassInterface *output) WARN_UNUSED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the buffer object.
|
||||||
|
*
|
||||||
|
* @param o object to free
|
||||||
|
*/
|
||||||
|
void StreamBuffer_Free (StreamBuffer *o);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,512 @@
|
||||||
|
/**
|
||||||
|
* @file dostest-attacker.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <misc/version.h>
|
||||||
|
#include <misc/offset.h>
|
||||||
|
#include <misc/open_standard_streams.h>
|
||||||
|
#include <misc/balloc.h>
|
||||||
|
#include <misc/loglevel.h>
|
||||||
|
#include <misc/minmax.h>
|
||||||
|
#include <structure/LinkedList1.h>
|
||||||
|
#include <base/BLog.h>
|
||||||
|
#include <system/BAddr.h>
|
||||||
|
#include <system/BReactor.h>
|
||||||
|
#include <system/BNetwork.h>
|
||||||
|
#include <system/BConnection.h>
|
||||||
|
#include <system/BSignal.h>
|
||||||
|
|
||||||
|
#include <generated/blog_channel_dostest_attacker.h>
|
||||||
|
|
||||||
|
#define PROGRAM_NAME "dostest-attacker"
|
||||||
|
|
||||||
|
// connection structure
|
||||||
|
struct connection {
|
||||||
|
int connected;
|
||||||
|
BConnector connector;
|
||||||
|
BConnection con;
|
||||||
|
StreamRecvInterface *recv_if;
|
||||||
|
uint8_t buf[512];
|
||||||
|
LinkedList1Node connections_list_node;
|
||||||
|
};
|
||||||
|
|
||||||
|
// command-line options
|
||||||
|
static struct {
|
||||||
|
int help;
|
||||||
|
int version;
|
||||||
|
char *connect_addr;
|
||||||
|
int max_connections;
|
||||||
|
int max_connecting;
|
||||||
|
int loglevel;
|
||||||
|
int loglevels[BLOG_NUM_CHANNELS];
|
||||||
|
} options;
|
||||||
|
|
||||||
|
// connect address
|
||||||
|
static BAddr connect_addr;
|
||||||
|
|
||||||
|
// reactor
|
||||||
|
static BReactor reactor;
|
||||||
|
|
||||||
|
// connections
|
||||||
|
static LinkedList1 connections_list;
|
||||||
|
static int num_connections;
|
||||||
|
static int num_connecting;
|
||||||
|
|
||||||
|
// timer for scheduling creation of more connections
|
||||||
|
static BTimer make_connections_timer;
|
||||||
|
|
||||||
|
static void print_help (const char *name);
|
||||||
|
static void print_version (void);
|
||||||
|
static int parse_arguments (int argc, char *argv[]);
|
||||||
|
static int process_arguments (void);
|
||||||
|
static void signal_handler (void *unused);
|
||||||
|
static int connection_new (void);
|
||||||
|
static void connection_free (struct connection *conn);
|
||||||
|
static void connection_logfunc (struct connection *conn);
|
||||||
|
static void connection_log (struct connection *conn, int level, const char *fmt, ...);
|
||||||
|
static void connection_connector_handler (struct connection *conn, int is_error);
|
||||||
|
static void connection_connection_handler (struct connection *conn, int event);
|
||||||
|
static void connection_recv_handler_done (struct connection *conn, int data_len);
|
||||||
|
static void make_connections_timer_handler (void *unused);
|
||||||
|
|
||||||
|
int main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc <= 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// open standard streams
|
||||||
|
open_standard_streams();
|
||||||
|
|
||||||
|
// parse command-line arguments
|
||||||
|
if (!parse_arguments(argc, argv)) {
|
||||||
|
fprintf(stderr, "Failed to parse arguments\n");
|
||||||
|
print_help(argv[0]);
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle --help and --version
|
||||||
|
if (options.help) {
|
||||||
|
print_version();
|
||||||
|
print_help(argv[0]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (options.version) {
|
||||||
|
print_version();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init loger
|
||||||
|
BLog_InitStderr();
|
||||||
|
|
||||||
|
// configure logger channels
|
||||||
|
for (int i = 0; i < BLOG_NUM_CHANNELS; i++) {
|
||||||
|
if (options.loglevels[i] >= 0) {
|
||||||
|
BLog_SetChannelLoglevel(i, options.loglevels[i]);
|
||||||
|
}
|
||||||
|
else if (options.loglevel >= 0) {
|
||||||
|
BLog_SetChannelLoglevel(i, options.loglevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION);
|
||||||
|
|
||||||
|
// initialize network
|
||||||
|
if (!BNetwork_GlobalInit()) {
|
||||||
|
BLog(BLOG_ERROR, "BNetwork_GlobalInit failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// process arguments
|
||||||
|
if (!process_arguments()) {
|
||||||
|
BLog(BLOG_ERROR, "Failed to process arguments");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init time
|
||||||
|
BTime_Init();
|
||||||
|
|
||||||
|
// init reactor
|
||||||
|
if (!BReactor_Init(&reactor)) {
|
||||||
|
BLog(BLOG_ERROR, "BReactor_Init failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup signal handler
|
||||||
|
if (!BSignal_Init(&reactor, signal_handler, NULL)) {
|
||||||
|
BLog(BLOG_ERROR, "BSignal_Init failed");
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init connections list
|
||||||
|
LinkedList1_Init(&connections_list);
|
||||||
|
num_connections = 0;
|
||||||
|
num_connecting = 0;
|
||||||
|
|
||||||
|
// init make connections timer
|
||||||
|
BTimer_Init(&make_connections_timer, 0, make_connections_timer_handler, NULL);
|
||||||
|
BReactor_SetTimer(&reactor, &make_connections_timer);
|
||||||
|
|
||||||
|
// enter event loop
|
||||||
|
BLog(BLOG_NOTICE, "entering event loop");
|
||||||
|
BReactor_Exec(&reactor);
|
||||||
|
|
||||||
|
// free connections
|
||||||
|
while (!LinkedList1_IsEmpty(&connections_list)) {
|
||||||
|
struct connection *conn = UPPER_OBJECT(LinkedList1_GetFirst(&connections_list), struct connection, connections_list_node);
|
||||||
|
connection_free(conn);
|
||||||
|
}
|
||||||
|
// free make connections timer
|
||||||
|
BReactor_RemoveTimer(&reactor, &make_connections_timer);
|
||||||
|
// free signal
|
||||||
|
BSignal_Finish();
|
||||||
|
fail2:
|
||||||
|
// free reactor
|
||||||
|
BReactor_Free(&reactor);
|
||||||
|
fail1:
|
||||||
|
// free logger
|
||||||
|
BLog(BLOG_NOTICE, "exiting");
|
||||||
|
BLog_Free();
|
||||||
|
fail0:
|
||||||
|
// finish debug objects
|
||||||
|
DebugObjectGlobal_Finish();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_help (const char *name)
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"Usage:\n"
|
||||||
|
" %s\n"
|
||||||
|
" [--help]\n"
|
||||||
|
" [--version]\n"
|
||||||
|
" --connect-addr <addr>\n"
|
||||||
|
" --max-connections <number>\n"
|
||||||
|
" --max-connecting <number>\n"
|
||||||
|
" [--loglevel <0-5/none/error/warning/notice/info/debug>]\n"
|
||||||
|
" [--channel-loglevel <channel-name> <0-5/none/error/warning/notice/info/debug>] ...\n"
|
||||||
|
"Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n",
|
||||||
|
name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_version (void)
|
||||||
|
{
|
||||||
|
printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_arguments (int argc, char *argv[])
|
||||||
|
{
|
||||||
|
options.help = 0;
|
||||||
|
options.version = 0;
|
||||||
|
options.connect_addr = NULL;
|
||||||
|
options.max_connections = -1;
|
||||||
|
options.max_connecting = -1;
|
||||||
|
options.loglevel = -1;
|
||||||
|
for (int i = 0; i < BLOG_NUM_CHANNELS; i++) {
|
||||||
|
options.loglevels[i] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
char *arg = argv[i];
|
||||||
|
if (!strcmp(arg, "--help")) {
|
||||||
|
options.help = 1;
|
||||||
|
}
|
||||||
|
else if (!strcmp(arg, "--version")) {
|
||||||
|
options.version = 1;
|
||||||
|
}
|
||||||
|
else if (!strcmp(arg, "--connect-addr")) {
|
||||||
|
if (1 >= argc - i) {
|
||||||
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
options.connect_addr = argv[i + 1];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if (!strcmp(arg, "--max-connections")) {
|
||||||
|
if (1 >= argc - i) {
|
||||||
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ((options.max_connections = atoi(argv[i + 1])) <= 0) {
|
||||||
|
fprintf(stderr, "%s: wrong argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if (!strcmp(arg, "--max-connecting")) {
|
||||||
|
if (1 >= argc - i) {
|
||||||
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ((options.max_connecting = atoi(argv[i + 1])) <= 0) {
|
||||||
|
fprintf(stderr, "%s: wrong argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if (!strcmp(arg, "--loglevel")) {
|
||||||
|
if (1 >= argc - i) {
|
||||||
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) {
|
||||||
|
fprintf(stderr, "%s: wrong argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if (!strcmp(arg, "--channel-loglevel")) {
|
||||||
|
if (2 >= argc - i) {
|
||||||
|
fprintf(stderr, "%s: requires two arguments\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int channel = BLogGlobal_GetChannelByName(argv[i + 1]);
|
||||||
|
if (channel < 0) {
|
||||||
|
fprintf(stderr, "%s: wrong channel argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int loglevel = parse_loglevel(argv[i + 2]);
|
||||||
|
if (loglevel < 0) {
|
||||||
|
fprintf(stderr, "%s: wrong loglevel argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
options.loglevels[channel] = loglevel;
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "unknown option: %s\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.help || options.version) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.connect_addr) {
|
||||||
|
fprintf(stderr, "--connect-addr missing\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.max_connections == -1) {
|
||||||
|
fprintf(stderr, "--max-connections missing\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.max_connecting == -1) {
|
||||||
|
fprintf(stderr, "--max-connecting missing\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int process_arguments (void)
|
||||||
|
{
|
||||||
|
// resolve listen address
|
||||||
|
if (!BAddr_Parse(&connect_addr, options.connect_addr, NULL, 0)) {
|
||||||
|
BLog(BLOG_ERROR, "connect addr: BAddr_Parse failed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void signal_handler (void *unused)
|
||||||
|
{
|
||||||
|
BLog(BLOG_NOTICE, "termination requested");
|
||||||
|
|
||||||
|
// exit event loop
|
||||||
|
BReactor_Quit(&reactor, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int connection_new (void)
|
||||||
|
{
|
||||||
|
// allocate structure
|
||||||
|
struct connection *conn = (struct connection *)malloc(sizeof(*conn));
|
||||||
|
if (!conn) {
|
||||||
|
BLog(BLOG_ERROR, "malloc failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set not connected
|
||||||
|
conn->connected = 0;
|
||||||
|
|
||||||
|
// init connector
|
||||||
|
if (!BConnector_Init(&conn->connector, connect_addr, &reactor, conn, (BConnector_handler)connection_connector_handler)) {
|
||||||
|
BLog(BLOG_ERROR, "BConnector_Init failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add to connections list
|
||||||
|
LinkedList1_Append(&connections_list, &conn->connections_list_node);
|
||||||
|
num_connections++;
|
||||||
|
num_connecting++;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail1:
|
||||||
|
free(conn);
|
||||||
|
fail0:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void connection_free (struct connection *conn)
|
||||||
|
{
|
||||||
|
// remove from connections list
|
||||||
|
LinkedList1_Remove(&connections_list, &conn->connections_list_node);
|
||||||
|
num_connections--;
|
||||||
|
if (!conn->connected) {
|
||||||
|
num_connecting--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn->connected) {
|
||||||
|
// free receive interface
|
||||||
|
BConnection_RecvAsync_Free(&conn->con);
|
||||||
|
|
||||||
|
// free connection
|
||||||
|
BConnection_Free(&conn->con);
|
||||||
|
}
|
||||||
|
|
||||||
|
// free connector
|
||||||
|
BConnector_Free(&conn->connector);
|
||||||
|
|
||||||
|
// free structure
|
||||||
|
free(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void connection_logfunc (struct connection *conn)
|
||||||
|
{
|
||||||
|
BLog_Append("%d connection (%p): ", num_connecting, (void *)conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void connection_log (struct connection *conn, int level, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list vl;
|
||||||
|
va_start(vl, fmt);
|
||||||
|
BLog_LogViaFuncVarArg((BLog_logfunc)connection_logfunc, conn, BLOG_CURRENT_CHANNEL, level, fmt, vl);
|
||||||
|
va_end(vl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void connection_connector_handler (struct connection *conn, int is_error)
|
||||||
|
{
|
||||||
|
ASSERT(!conn->connected)
|
||||||
|
|
||||||
|
// check for connection error
|
||||||
|
if (is_error) {
|
||||||
|
connection_log(conn, BLOG_INFO, "failed to connect");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init connection from connector
|
||||||
|
if (!BConnection_Init(&conn->con, BConnection_source_connector(&conn->connector), &reactor, conn, (BConnection_handler)connection_connection_handler)) {
|
||||||
|
connection_log(conn, BLOG_INFO, "BConnection_Init failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init receive interface
|
||||||
|
BConnection_RecvAsync_Init(&conn->con);
|
||||||
|
conn->recv_if = BConnection_RecvAsync_GetIf(&conn->con);
|
||||||
|
StreamRecvInterface_Receiver_Init(conn->recv_if, (StreamRecvInterface_handler_done)connection_recv_handler_done, conn);
|
||||||
|
|
||||||
|
// start receiving
|
||||||
|
StreamRecvInterface_Receiver_Recv(conn->recv_if, conn->buf, sizeof(conn->buf));
|
||||||
|
|
||||||
|
// no longer connecting
|
||||||
|
conn->connected = 1;
|
||||||
|
num_connecting--;
|
||||||
|
|
||||||
|
connection_log(conn, BLOG_INFO, "connected");
|
||||||
|
|
||||||
|
// schedule making connections (because of connecting limit)
|
||||||
|
BReactor_SetTimer(&reactor, &make_connections_timer);
|
||||||
|
return;
|
||||||
|
|
||||||
|
fail0:
|
||||||
|
// free connection
|
||||||
|
connection_free(conn);
|
||||||
|
|
||||||
|
// schedule making connections
|
||||||
|
BReactor_SetTimer(&reactor, &make_connections_timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void connection_connection_handler (struct connection *conn, int event)
|
||||||
|
{
|
||||||
|
ASSERT(conn->connected)
|
||||||
|
|
||||||
|
if (event == BCONNECTION_EVENT_RECVCLOSED) {
|
||||||
|
connection_log(conn, BLOG_INFO, "connection closed");
|
||||||
|
} else {
|
||||||
|
connection_log(conn, BLOG_INFO, "connection error");
|
||||||
|
}
|
||||||
|
|
||||||
|
// free connection
|
||||||
|
connection_free(conn);
|
||||||
|
|
||||||
|
// schedule making connections
|
||||||
|
BReactor_SetTimer(&reactor, &make_connections_timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void connection_recv_handler_done (struct connection *conn, int data_len)
|
||||||
|
{
|
||||||
|
ASSERT(conn->connected)
|
||||||
|
|
||||||
|
// receive more
|
||||||
|
StreamRecvInterface_Receiver_Recv(conn->recv_if, conn->buf, sizeof(conn->buf));
|
||||||
|
|
||||||
|
connection_log(conn, BLOG_INFO, "received %d bytes", data_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_connections_timer_handler (void *unused)
|
||||||
|
{
|
||||||
|
int make_num = bmin_int(options.max_connections - num_connections, options.max_connecting - num_connecting);
|
||||||
|
|
||||||
|
if (make_num <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BLog(BLOG_INFO, "making %d connections", make_num);
|
||||||
|
|
||||||
|
for (int i = 0; i < make_num; i++) {
|
||||||
|
if (!connection_new()) {
|
||||||
|
// can happen if fd limit is reached
|
||||||
|
BLog(BLOG_ERROR, "failed to make connection, waiting");
|
||||||
|
BReactor_SetTimerAfter(&reactor, &make_connections_timer, 10);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,567 @@
|
||||||
|
/**
|
||||||
|
* @file dostest-server.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifdef BADVPN_LINUX
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <misc/version.h>
|
||||||
|
#include <misc/offset.h>
|
||||||
|
#include <misc/open_standard_streams.h>
|
||||||
|
#include <misc/balloc.h>
|
||||||
|
#include <misc/loglevel.h>
|
||||||
|
#include <structure/LinkedList1.h>
|
||||||
|
#include <base/BLog.h>
|
||||||
|
#include <system/BAddr.h>
|
||||||
|
#include <system/BReactor.h>
|
||||||
|
#include <system/BNetwork.h>
|
||||||
|
#include <system/BConnection.h>
|
||||||
|
#include <system/BSignal.h>
|
||||||
|
#include "StreamBuffer.h"
|
||||||
|
|
||||||
|
#include <generated/blog_channel_dostest_server.h>
|
||||||
|
|
||||||
|
#define PROGRAM_NAME "dostest-server"
|
||||||
|
|
||||||
|
#ifdef BADVPN_LINUX
|
||||||
|
#ifndef SO_DOSDEF_PREPARE
|
||||||
|
#define SO_DOSDEF_PREPARE 48
|
||||||
|
#endif
|
||||||
|
#ifndef SO_DOSDEF_ACTIVATE
|
||||||
|
#define SO_DOSDEF_ACTIVATE 49
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BUF_SIZE 1024
|
||||||
|
|
||||||
|
// client structure
|
||||||
|
struct client {
|
||||||
|
BConnection con;
|
||||||
|
BAddr addr;
|
||||||
|
StreamBuffer buf;
|
||||||
|
BTimer disconnect_timer;
|
||||||
|
LinkedList1Node clients_list_node;
|
||||||
|
};
|
||||||
|
|
||||||
|
// command-line options
|
||||||
|
static struct {
|
||||||
|
int help;
|
||||||
|
int version;
|
||||||
|
char *listen_addr;
|
||||||
|
int max_clients;
|
||||||
|
int disconnect_time;
|
||||||
|
int defense_prepare_clients;
|
||||||
|
int defense_activate_clients;
|
||||||
|
int loglevel;
|
||||||
|
int loglevels[BLOG_NUM_CHANNELS];
|
||||||
|
} options;
|
||||||
|
|
||||||
|
// listen address
|
||||||
|
static BAddr listen_addr;
|
||||||
|
|
||||||
|
// reactor
|
||||||
|
static BReactor ss;
|
||||||
|
|
||||||
|
// listener
|
||||||
|
static BListener listener;
|
||||||
|
|
||||||
|
// clients
|
||||||
|
static LinkedList1 clients_list;
|
||||||
|
static int num_clients;
|
||||||
|
|
||||||
|
// defense status
|
||||||
|
static int defense_prepare;
|
||||||
|
static int defense_activate;
|
||||||
|
|
||||||
|
static void print_help (const char *name);
|
||||||
|
static void print_version (void);
|
||||||
|
static int parse_arguments (int argc, char *argv[]);
|
||||||
|
static int process_arguments (void);
|
||||||
|
static void signal_handler (void *unused);
|
||||||
|
static void listener_handler (void *unused);
|
||||||
|
static void client_free (struct client *client);
|
||||||
|
static void client_logfunc (struct client *client);
|
||||||
|
static void client_log (struct client *client, int level, const char *fmt, ...);
|
||||||
|
static void client_disconnect_timer_handler (struct client *client);
|
||||||
|
static void client_connection_handler (struct client *client, int event);
|
||||||
|
static void update_defense (void);
|
||||||
|
|
||||||
|
int main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc <= 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// open standard streams
|
||||||
|
open_standard_streams();
|
||||||
|
|
||||||
|
// parse command-line arguments
|
||||||
|
if (!parse_arguments(argc, argv)) {
|
||||||
|
fprintf(stderr, "Failed to parse arguments\n");
|
||||||
|
print_help(argv[0]);
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle --help and --version
|
||||||
|
if (options.help) {
|
||||||
|
print_version();
|
||||||
|
print_help(argv[0]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (options.version) {
|
||||||
|
print_version();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init loger
|
||||||
|
BLog_InitStderr();
|
||||||
|
|
||||||
|
// configure logger channels
|
||||||
|
for (int i = 0; i < BLOG_NUM_CHANNELS; i++) {
|
||||||
|
if (options.loglevels[i] >= 0) {
|
||||||
|
BLog_SetChannelLoglevel(i, options.loglevels[i]);
|
||||||
|
}
|
||||||
|
else if (options.loglevel >= 0) {
|
||||||
|
BLog_SetChannelLoglevel(i, options.loglevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION);
|
||||||
|
|
||||||
|
// initialize network
|
||||||
|
if (!BNetwork_GlobalInit()) {
|
||||||
|
BLog(BLOG_ERROR, "BNetwork_GlobalInit failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// process arguments
|
||||||
|
if (!process_arguments()) {
|
||||||
|
BLog(BLOG_ERROR, "Failed to process arguments");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init time
|
||||||
|
BTime_Init();
|
||||||
|
|
||||||
|
// init reactor
|
||||||
|
if (!BReactor_Init(&ss)) {
|
||||||
|
BLog(BLOG_ERROR, "BReactor_Init failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup signal handler
|
||||||
|
if (!BSignal_Init(&ss, signal_handler, NULL)) {
|
||||||
|
BLog(BLOG_ERROR, "BSignal_Init failed");
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize listener
|
||||||
|
if (!BListener_Init(&listener, listen_addr, &ss, NULL, listener_handler)) {
|
||||||
|
BLog(BLOG_ERROR, "Listener_Init failed");
|
||||||
|
goto fail3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init clients list
|
||||||
|
LinkedList1_Init(&clients_list);
|
||||||
|
num_clients = 0;
|
||||||
|
|
||||||
|
// clear defense state
|
||||||
|
defense_prepare = 0;
|
||||||
|
defense_activate = 0;
|
||||||
|
|
||||||
|
// update defense
|
||||||
|
update_defense();
|
||||||
|
|
||||||
|
// enter event loop
|
||||||
|
BLog(BLOG_NOTICE, "entering event loop");
|
||||||
|
BReactor_Exec(&ss);
|
||||||
|
|
||||||
|
// free clients
|
||||||
|
while (!LinkedList1_IsEmpty(&clients_list)) {
|
||||||
|
struct client *client = UPPER_OBJECT(LinkedList1_GetFirst(&clients_list), struct client, clients_list_node);
|
||||||
|
client_free(client);
|
||||||
|
}
|
||||||
|
// free listener
|
||||||
|
BListener_Free(&listener);
|
||||||
|
fail3:
|
||||||
|
// free signal
|
||||||
|
BSignal_Finish();
|
||||||
|
fail2:
|
||||||
|
// free reactor
|
||||||
|
BReactor_Free(&ss);
|
||||||
|
fail1:
|
||||||
|
// free logger
|
||||||
|
BLog(BLOG_NOTICE, "exiting");
|
||||||
|
BLog_Free();
|
||||||
|
fail0:
|
||||||
|
// finish debug objects
|
||||||
|
DebugObjectGlobal_Finish();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_help (const char *name)
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"Usage:\n"
|
||||||
|
" %s\n"
|
||||||
|
" [--help]\n"
|
||||||
|
" [--version]\n"
|
||||||
|
" --listen-addr <addr>\n"
|
||||||
|
" --max-clients <number>\n"
|
||||||
|
" --disconnect-time <milliseconds>\n"
|
||||||
|
" [--defense-prepare-clients <number>]\n"
|
||||||
|
" [--defense-activate-clients <number>]\n"
|
||||||
|
" [--loglevel <0-5/none/error/warning/notice/info/debug>]\n"
|
||||||
|
" [--channel-loglevel <channel-name> <0-5/none/error/warning/notice/info/debug>] ...\n"
|
||||||
|
"Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n",
|
||||||
|
name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_version (void)
|
||||||
|
{
|
||||||
|
printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_arguments (int argc, char *argv[])
|
||||||
|
{
|
||||||
|
options.help = 0;
|
||||||
|
options.version = 0;
|
||||||
|
options.listen_addr = NULL;
|
||||||
|
options.max_clients = -1;
|
||||||
|
options.disconnect_time = -1;
|
||||||
|
options.defense_prepare_clients = -1;
|
||||||
|
options.defense_activate_clients = -1;
|
||||||
|
options.loglevel = -1;
|
||||||
|
for (int i = 0; i < BLOG_NUM_CHANNELS; i++) {
|
||||||
|
options.loglevels[i] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
char *arg = argv[i];
|
||||||
|
if (!strcmp(arg, "--help")) {
|
||||||
|
options.help = 1;
|
||||||
|
}
|
||||||
|
else if (!strcmp(arg, "--version")) {
|
||||||
|
options.version = 1;
|
||||||
|
}
|
||||||
|
else if (!strcmp(arg, "--listen-addr")) {
|
||||||
|
if (1 >= argc - i) {
|
||||||
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
options.listen_addr = argv[i + 1];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if (!strcmp(arg, "--max-clients")) {
|
||||||
|
if (1 >= argc - i) {
|
||||||
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ((options.max_clients = atoi(argv[i + 1])) <= 0) {
|
||||||
|
fprintf(stderr, "%s: wrong argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if (!strcmp(arg, "--disconnect-time")) {
|
||||||
|
if (1 >= argc - i) {
|
||||||
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ((options.disconnect_time = atoi(argv[i + 1])) <= 0) {
|
||||||
|
fprintf(stderr, "%s: wrong argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if (!strcmp(arg, "--defense-prepare-clients")) {
|
||||||
|
if (1 >= argc - i) {
|
||||||
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ((options.defense_prepare_clients = atoi(argv[i + 1])) <= 0) {
|
||||||
|
fprintf(stderr, "%s: wrong argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if (!strcmp(arg, "--defense-activate-clients")) {
|
||||||
|
if (1 >= argc - i) {
|
||||||
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ((options.defense_activate_clients = atoi(argv[i + 1])) <= 0) {
|
||||||
|
fprintf(stderr, "%s: wrong argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if (!strcmp(arg, "--loglevel")) {
|
||||||
|
if (1 >= argc - i) {
|
||||||
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) {
|
||||||
|
fprintf(stderr, "%s: wrong argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if (!strcmp(arg, "--channel-loglevel")) {
|
||||||
|
if (2 >= argc - i) {
|
||||||
|
fprintf(stderr, "%s: requires two arguments\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int channel = BLogGlobal_GetChannelByName(argv[i + 1]);
|
||||||
|
if (channel < 0) {
|
||||||
|
fprintf(stderr, "%s: wrong channel argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int loglevel = parse_loglevel(argv[i + 2]);
|
||||||
|
if (loglevel < 0) {
|
||||||
|
fprintf(stderr, "%s: wrong loglevel argument\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
options.loglevels[channel] = loglevel;
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "unknown option: %s\n", arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.help || options.version) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.listen_addr) {
|
||||||
|
fprintf(stderr, "--listen-addr missing\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.max_clients == -1) {
|
||||||
|
fprintf(stderr, "--max-clients missing\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.disconnect_time == -1) {
|
||||||
|
fprintf(stderr, "--disconnect-time missing\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int process_arguments (void)
|
||||||
|
{
|
||||||
|
// resolve listen address
|
||||||
|
if (!BAddr_Parse(&listen_addr, options.listen_addr, NULL, 0)) {
|
||||||
|
BLog(BLOG_ERROR, "listen addr: BAddr_Parse failed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void signal_handler (void *unused)
|
||||||
|
{
|
||||||
|
BLog(BLOG_NOTICE, "termination requested");
|
||||||
|
|
||||||
|
// exit event loop
|
||||||
|
BReactor_Quit(&ss, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void listener_handler (void *unused)
|
||||||
|
{
|
||||||
|
if (num_clients == options.max_clients) {
|
||||||
|
BLog(BLOG_ERROR, "maximum number of clients reached");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate structure
|
||||||
|
struct client *client = (struct client *)malloc(sizeof(*client));
|
||||||
|
if (!client) {
|
||||||
|
BLog(BLOG_ERROR, "malloc failed");
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// accept client
|
||||||
|
if (!BConnection_Init(&client->con, BConnection_source_listener(&listener, &client->addr), &ss, client, (BConnection_handler)client_connection_handler)) {
|
||||||
|
BLog(BLOG_ERROR, "BConnection_Init failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init connection interfaces
|
||||||
|
BConnection_RecvAsync_Init(&client->con);
|
||||||
|
BConnection_SendAsync_Init(&client->con);
|
||||||
|
StreamRecvInterface *recv_if = BConnection_RecvAsync_GetIf(&client->con);
|
||||||
|
StreamPassInterface *send_if = BConnection_SendAsync_GetIf(&client->con);
|
||||||
|
|
||||||
|
// init stream buffer (to loop received data back to the client)
|
||||||
|
if (!StreamBuffer_Init(&client->buf, BUF_SIZE, recv_if, send_if)) {
|
||||||
|
BLog(BLOG_ERROR, "StreamBuffer_Init failed");
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init disconnect timer
|
||||||
|
BTimer_Init(&client->disconnect_timer, options.disconnect_time, (BTimer_handler)client_disconnect_timer_handler, client);
|
||||||
|
BReactor_SetTimer(&ss, &client->disconnect_timer);
|
||||||
|
|
||||||
|
// insert to clients list
|
||||||
|
LinkedList1_Append(&clients_list, &client->clients_list_node);
|
||||||
|
num_clients++;
|
||||||
|
|
||||||
|
client_log(client, BLOG_INFO, "connected");
|
||||||
|
BLog(BLOG_NOTICE, "%d clients", num_clients);
|
||||||
|
|
||||||
|
// update defense
|
||||||
|
update_defense();
|
||||||
|
return;
|
||||||
|
|
||||||
|
fail2:
|
||||||
|
BConnection_SendAsync_Free(&client->con);
|
||||||
|
BConnection_RecvAsync_Free(&client->con);
|
||||||
|
BConnection_Free(&client->con);
|
||||||
|
fail1:
|
||||||
|
free(client);
|
||||||
|
fail0:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void client_free (struct client *client)
|
||||||
|
{
|
||||||
|
// remove from clients list
|
||||||
|
LinkedList1_Remove(&clients_list, &client->clients_list_node);
|
||||||
|
num_clients--;
|
||||||
|
|
||||||
|
// free disconnect timer
|
||||||
|
BReactor_RemoveTimer(&ss, &client->disconnect_timer);
|
||||||
|
|
||||||
|
// free stream buffer
|
||||||
|
StreamBuffer_Free(&client->buf);
|
||||||
|
|
||||||
|
// free connection interfaces
|
||||||
|
BConnection_SendAsync_Free(&client->con);
|
||||||
|
BConnection_RecvAsync_Free(&client->con);
|
||||||
|
|
||||||
|
// free connection
|
||||||
|
BConnection_Free(&client->con);
|
||||||
|
|
||||||
|
// free structure
|
||||||
|
free(client);
|
||||||
|
|
||||||
|
BLog(BLOG_NOTICE, "%d clients", num_clients);
|
||||||
|
|
||||||
|
// update defense
|
||||||
|
update_defense();
|
||||||
|
}
|
||||||
|
|
||||||
|
void client_logfunc (struct client *client)
|
||||||
|
{
|
||||||
|
char addr[BADDR_MAX_PRINT_LEN];
|
||||||
|
BAddr_Print(&client->addr, addr);
|
||||||
|
|
||||||
|
BLog_Append("client (%s): ", addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void client_log (struct client *client, int level, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list vl;
|
||||||
|
va_start(vl, fmt);
|
||||||
|
BLog_LogViaFuncVarArg((BLog_logfunc)client_logfunc, client, BLOG_CURRENT_CHANNEL, level, fmt, vl);
|
||||||
|
va_end(vl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void client_disconnect_timer_handler (struct client *client)
|
||||||
|
{
|
||||||
|
client_log(client, BLOG_INFO, "timed out, disconnecting");
|
||||||
|
|
||||||
|
// free client
|
||||||
|
client_free(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
void client_connection_handler (struct client *client, int event)
|
||||||
|
{
|
||||||
|
if (event == BCONNECTION_EVENT_RECVCLOSED) {
|
||||||
|
client_log(client, BLOG_INFO, "client closed");
|
||||||
|
} else {
|
||||||
|
client_log(client, BLOG_INFO, "client error");
|
||||||
|
}
|
||||||
|
|
||||||
|
// free client
|
||||||
|
client_free(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_defense (void)
|
||||||
|
{
|
||||||
|
#ifdef BADVPN_LINUX
|
||||||
|
if (options.defense_prepare_clients != -1) {
|
||||||
|
int val = num_clients >= options.defense_prepare_clients;
|
||||||
|
int res = setsockopt(listener.fd, SOL_SOCKET, SO_DOSDEF_PREPARE, &val, sizeof(val));
|
||||||
|
if (res < 0) {
|
||||||
|
BLog(BLOG_ERROR, "failed to %s defense preparation", (val ? "enable" : "disable"));
|
||||||
|
} else {
|
||||||
|
if (!defense_prepare && val) {
|
||||||
|
BLog(BLOG_NOTICE, "defense preparation enabled");
|
||||||
|
}
|
||||||
|
else if (defense_prepare && !val) {
|
||||||
|
BLog(BLOG_NOTICE, "defense preparation disabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defense_prepare = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.defense_activate_clients != -1) {
|
||||||
|
int val = num_clients >= options.defense_activate_clients;
|
||||||
|
int res = setsockopt(listener.fd, SOL_SOCKET, SO_DOSDEF_ACTIVATE, &val, sizeof(val));
|
||||||
|
if (res < 0) {
|
||||||
|
BLog(BLOG_ERROR, "failed to %s defense activation", (val ? "enable" : "disable"));
|
||||||
|
} else {
|
||||||
|
if (!defense_activate && val) {
|
||||||
|
BLog(BLOG_NOTICE, "defense activation enabled");
|
||||||
|
}
|
||||||
|
else if (defense_activate && !val) {
|
||||||
|
BLog(BLOG_NOTICE, "defense activation disabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defense_activate = val;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
if (NOT EMSCRIPTEN)
|
||||||
|
add_executable(btimer_example btimer_example.c)
|
||||||
|
target_link_libraries(btimer_example system)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (BUILDING_PREDICATE)
|
||||||
|
add_executable(predicate_test predicate_test.c)
|
||||||
|
target_link_libraries(predicate_test predicate)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (NOT EMSCRIPTEN)
|
||||||
|
add_executable(fairqueue_test fairqueue_test.c)
|
||||||
|
target_link_libraries(fairqueue_test system flow)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
add_executable(indexedlist_test indexedlist_test.c)
|
||||||
|
|
||||||
|
if (BUILDING_SECURITY)
|
||||||
|
add_executable(fairqueue_test2 fairqueue_test2.c)
|
||||||
|
target_link_libraries(fairqueue_test2 system flow security)
|
||||||
|
|
||||||
|
add_executable(bavl_test bavl_test.c)
|
||||||
|
target_link_libraries(bavl_test security)
|
||||||
|
|
||||||
|
add_executable(savl_test savl_test.c)
|
||||||
|
target_link_libraries(savl_test security)
|
||||||
|
|
||||||
|
add_executable(bencryption_bench bencryption_bench.c)
|
||||||
|
target_link_libraries(bencryption_bench system security)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (BUILD_NCD)
|
||||||
|
add_executable(ncd_tokenizer_test ncd_tokenizer_test.c)
|
||||||
|
target_link_libraries(ncd_tokenizer_test ncdtokenizer)
|
||||||
|
|
||||||
|
add_executable(ncd_parser_test ncd_parser_test.c)
|
||||||
|
target_link_libraries(ncd_parser_test ncdconfigparser ncdvalgenerator ncdsugar)
|
||||||
|
|
||||||
|
add_executable(ncd_value_parser_test ncd_value_parser_test.c)
|
||||||
|
target_link_libraries(ncd_value_parser_test ncdvalparser ncdvalgenerator)
|
||||||
|
|
||||||
|
if (NOT EMSCRIPTEN)
|
||||||
|
add_executable(ncdinterfacemonitor_test ncdinterfacemonitor_test.c)
|
||||||
|
target_link_libraries(ncdinterfacemonitor_test ncdinterfacemonitor)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
add_executable(ncdval_test ncdval_test.c)
|
||||||
|
target_link_libraries(ncdval_test ncdval)
|
||||||
|
|
||||||
|
add_executable(ncdvalcons_test ncdvalcons_test.c)
|
||||||
|
target_link_libraries(ncdvalcons_test ncdvalcons ncdvalgenerator)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (BUILDING_UDEVMONITOR)
|
||||||
|
add_executable(ncdudevmonitor_test ncdudevmonitor_test.c)
|
||||||
|
target_link_libraries(ncdudevmonitor_test udevmonitor)
|
||||||
|
|
||||||
|
add_executable(ncdudevmanager_test ncdudevmanager_test.c)
|
||||||
|
target_link_libraries(ncdudevmanager_test udevmonitor)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (NOT WIN32 AND NOT EMSCRIPTEN)
|
||||||
|
add_executable(bprocess_example bprocess_example.c)
|
||||||
|
target_link_libraries(bprocess_example system)
|
||||||
|
|
||||||
|
add_executable(stdin_input stdin_input.c)
|
||||||
|
target_link_libraries(stdin_input system flow flowextra)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (BUILDING_DHCPCLIENT)
|
||||||
|
add_executable(dhcpclient_test dhcpclient_test.c)
|
||||||
|
target_link_libraries(dhcpclient_test dhcpclient)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (BUILDING_ARPPROBE)
|
||||||
|
add_executable(arpprobe_test arpprobe_test.c)
|
||||||
|
target_link_libraries(arpprobe_test arpprobe)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
add_executable(substring_test substring_test.c)
|
||||||
|
|
||||||
|
if (NOT WIN32)
|
||||||
|
add_executable(ipaddr6_test ipaddr6_test.c)
|
||||||
|
add_executable(parse_number_test parse_number_test.c)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (BUILDING_RANDOM)
|
||||||
|
add_executable(brandom2_test brandom2_test.c)
|
||||||
|
target_link_libraries(brandom2_test badvpn_random)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
add_executable(cavl_test cavl_test.c)
|
||||||
|
|
||||||
|
if (EMSCRIPTEN)
|
||||||
|
add_executable(emscripten_test emscripten_test.c)
|
||||||
|
target_link_libraries(emscripten_test system)
|
||||||
|
endif ()
|
|
@ -0,0 +1,79 @@
|
||||||
|
/**
|
||||||
|
* @file FastPacketSource.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _FASTPACKETSOURCE_H
|
||||||
|
#define _FASTPACKETSOURCE_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <flow/PacketPassInterface.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PacketPassInterface *output;
|
||||||
|
int psize;
|
||||||
|
uint8_t *data;
|
||||||
|
int data_len;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} FastPacketSource;
|
||||||
|
|
||||||
|
static void _FastPacketSource_output_handler_done (FastPacketSource *s)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&s->d_obj);
|
||||||
|
|
||||||
|
PacketPassInterface_Sender_Send(s->output, s->data, s->data_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FastPacketSource_Init (FastPacketSource *s, PacketPassInterface *output, uint8_t *data, int data_len, BPendingGroup *pg)
|
||||||
|
{
|
||||||
|
ASSERT(data_len >= 0)
|
||||||
|
ASSERT(data_len <= PacketPassInterface_GetMTU(output));
|
||||||
|
|
||||||
|
// init arguments
|
||||||
|
s->output = output;
|
||||||
|
s->data = data;
|
||||||
|
s->data_len = data_len;
|
||||||
|
|
||||||
|
// init output
|
||||||
|
PacketPassInterface_Sender_Init(s->output, (PacketPassInterface_handler_done)_FastPacketSource_output_handler_done, s);
|
||||||
|
|
||||||
|
// schedule send
|
||||||
|
PacketPassInterface_Sender_Send(s->output, s->data, s->data_len);
|
||||||
|
|
||||||
|
DebugObject_Init(&s->d_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FastPacketSource_Free (FastPacketSource *s)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&s->d_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,116 @@
|
||||||
|
/**
|
||||||
|
* @file RandomPacketSink.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _RANDOMPACKETSINK_H
|
||||||
|
#define _RANDOMPACKETSINK_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <security/BRandom.h>
|
||||||
|
#include <system/BReactor.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <flow/PacketPassInterface.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
BReactor *reactor;
|
||||||
|
PacketPassInterface input;
|
||||||
|
BTimer timer;
|
||||||
|
DebugObject d_obj;
|
||||||
|
} RandomPacketSink;
|
||||||
|
|
||||||
|
static void _RandomPacketSink_input_handler_send (RandomPacketSink *s, uint8_t *data, int data_len)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&s->d_obj);
|
||||||
|
|
||||||
|
printf("sink: send '");
|
||||||
|
size_t res = fwrite(data, data_len, 1, stdout);
|
||||||
|
B_USE(res)
|
||||||
|
|
||||||
|
uint8_t r;
|
||||||
|
BRandom_randomize(&r, sizeof(r));
|
||||||
|
if (r&(uint8_t)1) {
|
||||||
|
printf("' accepting\n");
|
||||||
|
PacketPassInterface_Done(&s->input);
|
||||||
|
} else {
|
||||||
|
printf("' delaying\n");
|
||||||
|
BReactor_SetTimer(s->reactor, &s->timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _RandomPacketSink_input_handler_requestcancel (RandomPacketSink *s)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&s->d_obj);
|
||||||
|
|
||||||
|
printf("sink: cancelled\n");
|
||||||
|
BReactor_RemoveTimer(s->reactor, &s->timer);
|
||||||
|
PacketPassInterface_Done(&s->input);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _RandomPacketSink_timer_handler (RandomPacketSink *s)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&s->d_obj);
|
||||||
|
|
||||||
|
PacketPassInterface_Done(&s->input);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RandomPacketSink_Init (RandomPacketSink *s, BReactor *reactor, int mtu, int ms)
|
||||||
|
{
|
||||||
|
// init arguments
|
||||||
|
s->reactor = reactor;
|
||||||
|
|
||||||
|
// init input
|
||||||
|
PacketPassInterface_Init(&s->input, mtu, (PacketPassInterface_handler_send)_RandomPacketSink_input_handler_send, s, BReactor_PendingGroup(reactor));
|
||||||
|
PacketPassInterface_EnableCancel(&s->input, (PacketPassInterface_handler_requestcancel)_RandomPacketSink_input_handler_requestcancel);
|
||||||
|
|
||||||
|
// init timer
|
||||||
|
BTimer_Init(&s->timer, ms, (BTimer_handler)_RandomPacketSink_timer_handler, s);
|
||||||
|
|
||||||
|
DebugObject_Init(&s->d_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RandomPacketSink_Free (RandomPacketSink *s)
|
||||||
|
{
|
||||||
|
DebugObject_Free(&s->d_obj);
|
||||||
|
|
||||||
|
// free timer
|
||||||
|
BReactor_RemoveTimer(s->reactor, &s->timer);
|
||||||
|
|
||||||
|
// free input
|
||||||
|
PacketPassInterface_Free(&s->input);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PacketPassInterface * RandomPacketSink_GetInput (RandomPacketSink *s)
|
||||||
|
{
|
||||||
|
DebugObject_Access(&s->d_obj);
|
||||||
|
|
||||||
|
return &s->input;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,97 @@
|
||||||
|
/**
|
||||||
|
* @file TimerPacketSink.h
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TIMERPACKETSINK_H
|
||||||
|
#define _TIMERPACKETSINK_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <system/BReactor.h>
|
||||||
|
#include <flow/PacketPassInterface.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
BReactor *reactor;
|
||||||
|
PacketPassInterface input;
|
||||||
|
BTimer timer;
|
||||||
|
} TimerPacketSink;
|
||||||
|
|
||||||
|
static void _TimerPacketSink_input_handler_send (TimerPacketSink *s, uint8_t *data, int data_len)
|
||||||
|
{
|
||||||
|
printf("sink: send '");
|
||||||
|
size_t res = fwrite(data, data_len, 1, stdout);
|
||||||
|
B_USE(res)
|
||||||
|
printf("'\n");
|
||||||
|
|
||||||
|
BReactor_SetTimer(s->reactor, &s->timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _TimerPacketSink_input_handler_requestcancel (TimerPacketSink *s)
|
||||||
|
{
|
||||||
|
printf("sink: cancelled\n");
|
||||||
|
|
||||||
|
BReactor_RemoveTimer(s->reactor, &s->timer);
|
||||||
|
PacketPassInterface_Done(&s->input);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _TimerPacketSink_timer_handler (TimerPacketSink *s)
|
||||||
|
{
|
||||||
|
printf("sink: done\n");
|
||||||
|
|
||||||
|
PacketPassInterface_Done(&s->input);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void TimerPacketSink_Init (TimerPacketSink *s, BReactor *reactor, int mtu, int ms)
|
||||||
|
{
|
||||||
|
// init arguments
|
||||||
|
s->reactor = reactor;
|
||||||
|
|
||||||
|
// init input
|
||||||
|
PacketPassInterface_Init(&s->input, mtu, (PacketPassInterface_handler_send)_TimerPacketSink_input_handler_send, s, BReactor_PendingGroup(s->reactor));
|
||||||
|
PacketPassInterface_EnableCancel(&s->input, (PacketPassInterface_handler_requestcancel)_TimerPacketSink_input_handler_requestcancel);
|
||||||
|
|
||||||
|
// init timer
|
||||||
|
BTimer_Init(&s->timer, ms, (BTimer_handler)_TimerPacketSink_timer_handler, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void TimerPacketSink_Free (TimerPacketSink *s)
|
||||||
|
{
|
||||||
|
// free timer
|
||||||
|
BReactor_RemoveTimer(s->reactor, &s->timer);
|
||||||
|
|
||||||
|
// free input
|
||||||
|
PacketPassInterface_Free(&s->input);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PacketPassInterface * TimerPacketSink_GetInput (TimerPacketSink *s)
|
||||||
|
{
|
||||||
|
return &s->input;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,131 @@
|
||||||
|
/**
|
||||||
|
* @file arpprobe_test.c
|
||||||
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the author nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include <misc/debug.h>
|
||||||
|
#include <base/DebugObject.h>
|
||||||
|
#include <base/BLog.h>
|
||||||
|
#include <system/BReactor.h>
|
||||||
|
#include <system/BSignal.h>
|
||||||
|
#include <system/BTime.h>
|
||||||
|
#include <system/BNetwork.h>
|
||||||
|
#include <arpprobe/BArpProbe.h>
|
||||||
|
|
||||||
|
BReactor reactor;
|
||||||
|
BArpProbe arpprobe;
|
||||||
|
|
||||||
|
static void signal_handler (void *user);
|
||||||
|
static void arpprobe_handler (void *unused, int event);
|
||||||
|
|
||||||
|
int main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc <= 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc != 3) {
|
||||||
|
printf("Usage: %s <interface> <addr>\n", argv[0]);
|
||||||
|
goto fail0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ifname = argv[1];
|
||||||
|
uint32_t addr = inet_addr(argv[2]);
|
||||||
|
|
||||||
|
BTime_Init();
|
||||||
|
|
||||||
|
BLog_InitStdout();
|
||||||
|
|
||||||
|
if (!BNetwork_GlobalInit()) {
|
||||||
|
DEBUG("BNetwork_GlobalInit failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!BReactor_Init(&reactor)) {
|
||||||
|
DEBUG("BReactor_Init failed");
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!BSignal_Init(&reactor, signal_handler, NULL)) {
|
||||||
|
DEBUG("BSignal_Init failed");
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!BArpProbe_Init(&arpprobe, ifname, addr, &reactor, NULL, arpprobe_handler)) {
|
||||||
|
DEBUG("BArpProbe_Init failed");
|
||||||
|
goto fail3;
|
||||||
|
}
|
||||||
|
|
||||||
|
BReactor_Exec(&reactor);
|
||||||
|
|
||||||
|
BArpProbe_Free(&arpprobe);
|
||||||
|
fail3:
|
||||||
|
BSignal_Finish();
|
||||||
|
fail2:
|
||||||
|
BReactor_Free(&reactor);
|
||||||
|
fail1:
|
||||||
|
BLog_Free();
|
||||||
|
fail0:
|
||||||
|
DebugObjectGlobal_Finish();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void signal_handler (void *user)
|
||||||
|
{
|
||||||
|
DEBUG("termination requested");
|
||||||
|
|
||||||
|
BReactor_Quit(&reactor, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void arpprobe_handler (void *unused, int event)
|
||||||
|
{
|
||||||
|
switch (event) {
|
||||||
|
case BARPPROBE_EVENT_EXIST: {
|
||||||
|
printf("ARPPROBE: exist\n");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case BARPPROBE_EVENT_NOEXIST: {
|
||||||
|
printf("ARPPROBE: noexist\n");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case BARPPROBE_EVENT_ERROR: {
|
||||||
|
printf("ARPPROBE: error\n");
|
||||||
|
|
||||||
|
// exit reactor
|
||||||
|
BReactor_Quit(&reactor, 0);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue