Merge branch 'ony-dev' into v15-dev

This commit is contained in:
Nathan Freitas 2015-02-02 13:43:54 -05:00
commit a78e458a43
1020 changed files with 235981 additions and 2908 deletions

View File

@ -5,6 +5,12 @@
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
<triggers>clean,full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.jsdt.core.javascriptValidator</name>
<arguments>
@ -30,10 +36,20 @@
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</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>
</projectDescription>

View File

@ -3,17 +3,17 @@
package="org.torproject.android"
android:versionName="14.1.4-noPIE"
android:versionCode="132"
android:installLocation="auto"
android:installLocation="auto"
>
<uses-sdk android:minSdkVersion="9" android:maxSdkVersion="20" android:targetSdkVersion="19"/>
<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.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>
<uses-permission android:name="org.torproject.android.MANAGE_TOR"/>
<uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>
<uses-permission android:name="org.torproject.android.MANAGE_TOR"/>
<application android:name=".OrbotApp" android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
@ -52,11 +52,11 @@
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="org.torproject.android.REQUEST_HS_PORT" />
<action android:name="org.torproject.android.REQUEST_HS_PORT" />
</intent-filter>
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="org.torproject.android.START_TOR" />
<action android:name="org.torproject.android.START_TOR" />
</intent-filter>
<!--
@ -76,56 +76,56 @@
<!--
<activity android:name=".OrbotDiagnosticsActivity" android:label="OrbotDiag"/>
-->
<activity
android:name=".service.DummyActivity"
android:theme="@android:style/Theme.NoDisplay"
android:enabled="true"
android:allowTaskReparenting="true"
android:noHistory="true"
android:excludeFromRecents="true"
android:alwaysRetainTaskState="false"
android:stateNotNeeded="true"
android:clearTaskOnLaunch="true"
android:finishOnTaskLaunch="true"
/>
<activity android:name=".wizard.LotsaText" android:exported="false"/>
<activity android:name=".wizard.Permissions" android:exported="false"/>
<activity android:name=".wizard.TipsAndTricks" android:exported="false"/>
<activity android:name=".wizard.ConfigureTransProxy" android:exported="false"/>
<activity android:name=".wizard.ChooseLocaleWizardActivity" android:exported="false"/>
<activity android:name=".settings.SettingsPreferences" android:label="@string/app_name"/>
<activity
android:name=".service.DummyActivity"
android:theme="@android:style/Theme.NoDisplay"
android:enabled="true"
android:allowTaskReparenting="true"
android:noHistory="true"
android:excludeFromRecents="true"
android:alwaysRetainTaskState="false"
android:stateNotNeeded="true"
android:clearTaskOnLaunch="true"
android:finishOnTaskLaunch="true"
/>
<activity android:name=".wizard.LotsaText" android:exported="false"/>
<activity android:name=".wizard.Permissions" android:exported="false"/>
<activity android:name=".wizard.TipsAndTricks" android:exported="false"/>
<activity android:name=".wizard.ConfigureTransProxy" android:exported="false"/>
<activity android:name=".wizard.ChooseLocaleWizardActivity" android:exported="false"/>
<activity android:name=".settings.SettingsPreferences" android:label="@string/app_name"/>
<activity android:name=".settings.AppManager" android:label="@string/app_name"/>
<service android:enabled="true"
android:name=".service.TorService"
android:permission="org.torproject.android.MANAGE_TOR"
android:stopWithTask="false"
>
<intent-filter>
<action android:name="org.torproject.android.service.ITorService" />
<action android:name="org.torproject.android.service.TOR_SERVICE" />
<service android:enabled="true"
android:name=".service.TorService"
android:permission="org.torproject.android.MANAGE_TOR"
android:stopWithTask="false"
>
<intent-filter>
<action android:name="org.torproject.android.service.ITorService" />
<action android:name="org.torproject.android.service.TOR_SERVICE" />
</intent-filter>
</service>
<receiver android:name=".OnBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="android.intent.action.MEDIA_MOUNTED"/>
</intent-filter>
</receiver>
</service>
<receiver android:name=".OnBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="android.intent.action.MEDIA_MOUNTED"/>
</intent-filter>
</receiver>
<service android:name="org.torproject.android.vpn.OrbotVpnService"
android:permission="android.permission.BIND_VPN_SERVICE">
<intent-filter>
<action android:name="android.net.VpnService"/>
</intent-filter>
</service>
</application>

View File

@ -3,17 +3,17 @@
package="org.torproject.android"
android:versionName="14.1.4-PIE"
android:versionCode="133"
android:installLocation="auto"
android:installLocation="auto"
>
<uses-sdk android:minSdkVersion="21" android:maxSdkVersion="21" android:targetSdkVersion="21"/>
<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.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>
<uses-permission android:name="org.torproject.android.MANAGE_TOR"/>
<uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>
<uses-permission android:name="org.torproject.android.MANAGE_TOR"/>
<application android:name=".OrbotApp" android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
@ -52,11 +52,11 @@
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="org.torproject.android.REQUEST_HS_PORT" />
<action android:name="org.torproject.android.REQUEST_HS_PORT" />
</intent-filter>
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="org.torproject.android.START_TOR" />
<action android:name="org.torproject.android.START_TOR" />
</intent-filter>
<!--
@ -76,56 +76,56 @@
<!--
<activity android:name=".OrbotDiagnosticsActivity" android:label="OrbotDiag"/>
-->
<activity
android:name=".service.DummyActivity"
android:theme="@android:style/Theme.NoDisplay"
android:enabled="true"
android:allowTaskReparenting="true"
android:noHistory="true"
android:excludeFromRecents="true"
android:alwaysRetainTaskState="false"
android:stateNotNeeded="true"
android:clearTaskOnLaunch="true"
android:finishOnTaskLaunch="true"
/>
<activity android:name=".wizard.LotsaText" android:exported="false"/>
<activity android:name=".wizard.Permissions" android:exported="false"/>
<activity android:name=".wizard.TipsAndTricks" android:exported="false"/>
<activity android:name=".wizard.ConfigureTransProxy" android:exported="false"/>
<activity android:name=".wizard.ChooseLocaleWizardActivity" android:exported="false"/>
<activity android:name=".settings.SettingsPreferences" android:label="@string/app_name"/>
<activity
android:name=".service.DummyActivity"
android:theme="@android:style/Theme.NoDisplay"
android:enabled="true"
android:allowTaskReparenting="true"
android:noHistory="true"
android:excludeFromRecents="true"
android:alwaysRetainTaskState="false"
android:stateNotNeeded="true"
android:clearTaskOnLaunch="true"
android:finishOnTaskLaunch="true"
/>
<activity android:name=".wizard.LotsaText" android:exported="false"/>
<activity android:name=".wizard.Permissions" android:exported="false"/>
<activity android:name=".wizard.TipsAndTricks" android:exported="false"/>
<activity android:name=".wizard.ConfigureTransProxy" android:exported="false"/>
<activity android:name=".wizard.ChooseLocaleWizardActivity" android:exported="false"/>
<activity android:name=".settings.SettingsPreferences" android:label="@string/app_name"/>
<activity android:name=".settings.AppManager" android:label="@string/app_name"/>
<service android:enabled="true"
android:name=".service.TorService"
android:permission="org.torproject.android.MANAGE_TOR"
android:stopWithTask="false"
>
<intent-filter>
<action android:name="org.torproject.android.service.ITorService" />
<action android:name="org.torproject.android.service.TOR_SERVICE" />
<service android:enabled="true"
android:name=".service.TorService"
android:permission="org.torproject.android.MANAGE_TOR"
android:stopWithTask="false"
>
<intent-filter>
<action android:name="org.torproject.android.service.ITorService" />
<action android:name="org.torproject.android.service.TOR_SERVICE" />
</intent-filter>
</service>
<receiver android:name=".OnBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="android.intent.action.MEDIA_MOUNTED"/>
</intent-filter>
</receiver>
</service>
<receiver android:name=".OnBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="android.intent.action.MEDIA_MOUNTED"/>
</intent-filter>
</receiver>
<service android:name="org.torproject.android.vpn.OrbotVpnService"
android:permission="android.permission.BIND_VPN_SERVICE">
<intent-filter>
<action android:name="android.net.VpnService"/>
</intent-filter>
</service>
</application>

View File

@ -8,16 +8,12 @@
<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>
<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="org.torproject.android.MANAGE_TOR"/>
<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:description="@string/app_description"
android:configChanges="locale|orientation|screenSize"
@ -54,11 +50,11 @@
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="org.torproject.android.REQUEST_HS_PORT" />
<action android:name="org.torproject.android.REQUEST_HS_PORT" />
</intent-filter>
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="org.torproject.android.START_TOR" />
<action android:name="org.torproject.android.START_TOR" />
</intent-filter>
</activity>
@ -163,7 +159,7 @@
<action android:name="android.net.VpnService"/>
</intent-filter>
</service>
</application>

View File

@ -1,3 +1,3 @@
Manifest-Version: 1.0
Sealed: true
Manifest-Version: 1.0
Sealed: true

6
external/Makefile vendored
View File

@ -16,14 +16,14 @@ EXTERNAL_ROOT := $(shell pwd)
# user building this will have to manually set NDK_PROCESSOR or NDK_TOOLCHAIN.
CPU := $(shell uname -m)
ifeq ($(CPU),x86_64)
NDK_PROCESSOR=x86_64
NDK_PROCESSOR=x86
else
NDK_PROCESSOR=x86
endif
# Android NDK setup
NDK_BASE ?= /opt/android-ndk
NDK_PLATFORM_LEVEL ?= 9
NDK_PLATFORM_LEVEL ?= 19
NDK_ABI ?= arm
NDK_TOOLCHAIN_VERSION=4.8
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
# PIEFLAGS for SDK 16/Android L must be set to -fPIE -pie
PIEFLAGS? =
PIEFLAGS = -fPIE -pie
CFLAGS = -DANDROID $(TARGET_CFLAGS) $(PIEFLAGS)
LDFLAGS = -llog $(TARGET_LDFLAGS) $(PIEFLAGS)

View File

@ -2,10 +2,10 @@
<classpath>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<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="lib" path="libs/android-support-v4.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"/>
</classpath>

75
external/badvpn_dns/Android.mk vendored Normal file
View File

@ -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)

408
external/badvpn_dns/CMakeLists.txt vendored Normal file
View File

@ -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 ()

24
external/badvpn_dns/COPYING vendored Normal file
View File

@ -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.

216
external/badvpn_dns/ChangeLog vendored Normal file
View File

@ -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.

76
external/badvpn_dns/INSTALL vendored Normal file
View File

@ -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)).

72
external/badvpn_dns/INSTALL-WINDOWS vendored Normal file
View File

@ -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

359
external/badvpn_dns/arpprobe/BArpProbe.c vendored Normal file
View File

@ -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);
}

View File

@ -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

View File

@ -0,0 +1 @@
badvpn_add_library(arpprobe "base;system;flow" "" BArpProbe.c)

324
external/badvpn_dns/badvpn.7 vendored Normal file
View File

@ -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>

96
external/badvpn_dns/base/BLog.c vendored Normal file
View File

@ -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 ====

402
external/badvpn_dns/base/BLog.h vendored Normal file
View File

@ -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

150
external/badvpn_dns/base/BLog_syslog.c vendored Normal file
View File

@ -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;
}

42
external/badvpn_dns/base/BLog_syslog.h vendored Normal file
View File

@ -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

101
external/badvpn_dns/base/BMutex.h vendored Normal file
View File

@ -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

205
external/badvpn_dns/base/BPending.c vendored Normal file
View File

@ -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);
}

250
external/badvpn_dns/base/BPending.h vendored Normal file
View File

@ -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

View File

@ -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

13
external/badvpn_dns/base/CMakeLists.txt vendored Normal file
View File

@ -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}")

39
external/badvpn_dns/base/DebugObject.c vendored Normal file
View File

@ -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

147
external/badvpn_dns/base/DebugObject.h vendored Normal file
View File

@ -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

145
external/badvpn_dns/blog_channels.txt vendored Normal file
View File

@ -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

View File

@ -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");
}

View 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);
}

85
external/badvpn_dns/bproto/BProto.h vendored Normal file
View File

@ -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

View File

@ -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
);
}.

View File

@ -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,
),
);
}

View File

@ -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");
}

View 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();
}

View File

@ -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
)

324
external/badvpn_dns/client/DPReceive.c vendored Normal file
View File

@ -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;
}

98
external/badvpn_dns/client/DPReceive.h vendored Normal file
View File

@ -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

307
external/badvpn_dns/client/DPRelay.c vendored Normal file
View File

@ -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;
}

89
external/badvpn_dns/client/DPRelay.h vendored Normal file
View File

@ -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

566
external/badvpn_dns/client/DataProto.c vendored Normal file
View File

@ -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);
}

237
external/badvpn_dns/client/DataProto.h vendored Normal file
View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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:;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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

433
external/badvpn_dns/client/PeerChat.c vendored Normal file
View File

@ -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);
}

123
external/badvpn_dns/client/PeerChat.h vendored Normal file
View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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>

2997
external/badvpn_dns/client/client.c vendored Normal file

File diff suppressed because it is too large Load Diff

193
external/badvpn_dns/client/client.h vendored Normal file
View File

@ -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;
};

View File

@ -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.

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

112
external/badvpn_dns/compile-tun2sock.sh vendored Executable file
View File

@ -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

84
external/badvpn_dns/compile-udpgw.sh vendored Executable file
View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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 ()

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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)

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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
}

View File

@ -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 ()

View File

@ -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

Some files were not shown because too many files have changed in this diff Show More