summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rw-r--r--client/client.vcxproj159
-rw-r--r--client/client.vcxproj.filters36
-rw-r--r--client/client_windows.cpp46
-rw-r--r--client/connect.hpp130
-rw-r--r--client/err.hpp22
-rw-r--r--client/strings.hpp163
-rw-r--r--client/util.hpp23
7 files changed, 579 insertions, 0 deletions
diff --git a/client/client.vcxproj b/client/client.vcxproj
new file mode 100644
index 0000000..7827d6f
--- /dev/null
+++ b/client/client.vcxproj
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{E877E475-A428-4FBC-AF71-378AFB92B706}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>client</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <LanguageStandard>stdcpp17</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <LanguageStandard>stdcpp17</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="client_windows.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="connect.hpp" />
+ <ClInclude Include="err.hpp" />
+ <ClInclude Include="strings.hpp" />
+ <ClInclude Include="util.hpp" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/client/client.vcxproj.filters b/client/client.vcxproj.filters
new file mode 100644
index 0000000..a0e485b
--- /dev/null
+++ b/client/client.vcxproj.filters
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="client_windows.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="connect.hpp">
+ <Filter>Source Files</Filter>
+ </ClInclude>
+ <ClInclude Include="err.hpp">
+ <Filter>Source Files</Filter>
+ </ClInclude>
+ <ClInclude Include="strings.hpp">
+ <Filter>Source Files</Filter>
+ </ClInclude>
+ <ClInclude Include="util.hpp">
+ <Filter>Source Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/client/client_windows.cpp b/client/client_windows.cpp
new file mode 100644
index 0000000..76bd776
--- /dev/null
+++ b/client/client_windows.cpp
@@ -0,0 +1,46 @@
+#include <Windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <iostream>
+
+#pragma comment( lib, "ws2_32.lib" )
+
+#include "connect.hpp"
+
+int main( ) {
+ std::string ip;
+ std::cin >> ip;
+
+ client::c_connect c( ip.c_str( ) );
+ if( !c.setup( ) )
+ return 1;
+
+ if( !c.connect( ) )
+ return 2;
+
+
+ const char* yes = "hello server";
+ char buf[ 255 ];
+ memcpy( buf, yes, strlen( yes ) );
+
+ c.send_msg( ( uint8_t* )( buf ), strlen( yes ) );
+ printf( "message sent\n" );
+
+ auto msg = c.get_msg( );
+ while( !msg.size( ) ) {
+ Sleep( 1 );
+ }
+
+ printf( "[message received]: " );
+ for( auto& it : msg )
+ printf( "%c", it );
+
+ printf( "\n" );
+
+ c.send_msg( ( uint8_t* )( buf ), strlen( yes ) );
+
+ c.~c_connect( );
+ system( "pause" );
+ return 0;
+}
diff --git a/client/connect.hpp b/client/connect.hpp
new file mode 100644
index 0000000..8b36687
--- /dev/null
+++ b/client/connect.hpp
@@ -0,0 +1,130 @@
+#pragma once
+
+#include <Windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#pragma comment( lib, "ws2_32.lib" )
+
+#include <vector>
+
+#include "err.hpp"
+#include "util.hpp"
+
+/*
+protocol rules:
+every msg must be xored
+first byte is the xorkey
+
+*/
+
+namespace client
+{
+ constexpr auto PORT_NUM = 6969;
+ constexpr auto BUFFER_SIZE = 255;
+
+ class c_connect {
+ public:
+ c_connect( const char* ip ) :
+ m_ip( inet_addr( ip ) ) { }
+
+ ~c_connect( ) {
+ if( m_socket )
+ closesocket( m_socket );
+
+ WSACleanup( );
+ }
+
+ bool setup( ) {
+ int code{ };
+
+ if( WSAStartup( MAKEWORD( 2, 2 ), &m_wsdata ) )
+ code = err::ERR_WSA;
+ else {
+ m_socket = socket( AF_INET, SOCK_STREAM, 0 );
+ if( m_socket == INVALID_SOCKET )
+ code = err::ERR_WSA;
+ }
+
+ if( code != err::ERR_NONE ) {
+ MessageBoxA( nullptr, err::translate_err( code ), "", MB_OK );
+ return false;
+ }
+
+ return true;
+ }
+
+ bool connect( ) {
+ sockaddr_in server_address{ };
+ int code{ };
+
+ server_address.sin_addr.s_addr = m_ip;
+ server_address.sin_port = htons( PORT_NUM );
+ server_address.sin_family = AF_INET;
+
+ code = ::connect( m_socket, ( sockaddr* )( &server_address ),
+ sizeof( server_address ) );
+
+ if( code == -1 ) {
+ MessageBoxA( nullptr, err::translate_err( err::ERR_CONNECT ), "", MB_OK );
+ return false;
+ }
+
+ return true;
+ }
+
+ void decode_buffer( uint8_t* buf, size_t length ) {
+ auto key = buf[ 0 ];
+ for( size_t i{ 1 }; i < length; ++i )
+ buf[ i ] ^= key;
+ }
+
+ std::vector< uint8_t > get_msg( ) {
+ std::vector< uint8_t > ret;
+ char buffer[ BUFFER_SIZE ];
+ int received = 0;
+
+ while( true ) {
+ received = recv( m_socket, buffer, BUFFER_SIZE, 0 );
+ if( received < 0 )
+ break;
+
+ for( int i{ }; i < received; ++i )
+ ret.push_back( buffer[ i ] );
+
+ if( received < BUFFER_SIZE )
+ break;
+ }
+
+ if( ret.size( ) ) {
+ decode_buffer( ret.data( ), ret.size( ) );
+ ret.erase( ret.begin( ) );
+ }
+ return ret;
+ }
+
+ void send_msg( const uint8_t* msg, size_t length ) {
+ auto new_buffer = ( uint8_t* )( malloc( length + 1 ) );
+ auto key = util::random_number( 0, 255 ) & 0xff;
+
+ new_buffer[ 0 ] = key;
+ memcpy( new_buffer + 1,
+ msg,
+ length );
+
+ for( size_t i = 1; i < length + 1; ++i ) {
+ new_buffer[ i ] ^= key;
+ }
+
+ send( m_socket, ( char* )new_buffer, length + 1, 0 );
+
+ free( new_buffer );
+ }
+
+ private:
+ SOCKET m_socket;
+ WSADATA m_wsdata;
+ int m_ip;
+ };
+} \ No newline at end of file
diff --git a/client/err.hpp b/client/err.hpp
new file mode 100644
index 0000000..5a6691d
--- /dev/null
+++ b/client/err.hpp
@@ -0,0 +1,22 @@
+#pragma once
+#include "strings.hpp"
+
+namespace err
+{
+ enum ErrCode_t {
+ ERR_NONE = 0,
+ ERR_WSA = 1,
+ ERR_CONNECT = 2,
+ };
+
+ const char* translate_err( int code ) {
+ switch( code ) {
+ case ERR_WSA:
+ return xors( "socket error" );
+ case ERR_CONNECT:
+ return xors( "connection error" );
+ }
+
+ return xors( "unknown error" );
+ }
+} \ No newline at end of file
diff --git a/client/strings.hpp b/client/strings.hpp
new file mode 100644
index 0000000..382ddb2
--- /dev/null
+++ b/client/strings.hpp
@@ -0,0 +1,163 @@
+//--------------------------------------------------------------------------------
+//-- XorCompileTime.hpp
+//
+// Author: frk
+// Date: 12.12.2015
+//
+//--------------------------------------------------------------------------------
+
+#pragma once
+#include <string>
+#include <array>
+#include <cstdarg>
+
+#define BEGIN_NAMESPACE( x ) namespace x {
+#define END_NAMESPACE }
+
+BEGIN_NAMESPACE( strenc )
+
+constexpr auto time = __TIME__;
+constexpr auto seed = static_cast< int >( time[ 7 ] ) + static_cast< int >( time[ 6 ] ) * 10 + static_cast< int >( time[ 4 ] ) * 60 + static_cast< int >( time[ 3 ] ) * 600 + static_cast< int >( time[ 1 ] ) * 3600 + static_cast< int >( time[ 0 ] ) * 36000;
+
+// 1988, Stephen Park and Keith Miller
+// "Random Number Generators: Good Ones Are Hard To Find", considered as "minimal standard"
+// Park-Miller 31 bit pseudo-random number generator, implemented with G. Carta's optimisation:
+// with 32-bit math and without division
+
+template < int N >
+struct RandomGenerator {
+private:
+ static constexpr unsigned a = 16807; // 7^5
+ static constexpr unsigned m = 2147483647; // 2^31 - 1
+
+ static constexpr unsigned s = RandomGenerator< N - 1 >::value;
+ static constexpr unsigned lo = a * ( s & 0xFFFF ); // Multiply lower 16 bits by 16807
+ static constexpr unsigned hi = a * ( s >> 16 ); // Multiply higher 16 bits by 16807
+ static constexpr unsigned lo2 = lo + ( ( hi & 0x7FFF ) << 16 ); // Combine lower 15 bits of hi with lo's upper bits
+ static constexpr unsigned hi2 = hi >> 15; // Discard lower 15 bits of hi
+ static constexpr unsigned lo3 = lo2 + hi;
+
+public:
+ static constexpr unsigned max = m;
+ static constexpr unsigned value = lo3 > m ? lo3 - m : lo3;
+};
+
+template <>
+struct RandomGenerator< 0 > {
+ static constexpr unsigned value = seed;
+};
+
+template < int N, int M >
+struct RandomInt {
+ static constexpr auto value = RandomGenerator< N + 1 >::value % M;
+};
+
+template < int N >
+struct RandomChar {
+ static const char value = static_cast< char >( 1 + RandomInt< N, 0x7F - 1 >::value );
+};
+
+template < size_t N, int K >
+struct XorString {
+private:
+ const char _key;
+ std::array< char, N + 1 > _encrypted;
+ bool decrypted = false;
+
+ constexpr char enc( char c ) const {
+ return c ^ _key;
+ }
+
+ char dec( char c ) const {
+ return c ^ _key;
+ }
+
+public:
+ template < size_t... Is >
+ constexpr __forceinline XorString( const char* const str, std::index_sequence< Is... > ) : _key( RandomChar< K >::value ), _encrypted{ enc( str[ Is ] )... } {
+ }
+
+ __forceinline const char* decrypt( void ) {
+ if( !decrypted ) {
+ for( size_t i = 0; i < N; ++i ) {
+ _encrypted[ i ] = dec( _encrypted[ i ] );
+ }
+ _encrypted[ N ] = '\0';
+ decrypted = true;
+ }
+
+ return _encrypted.data( );
+ }
+};
+
+//--------------------------------------------------------------------------------
+//-- Note: XorStr will __NOT__ work directly with functions like printf.
+// To work with them you need a wrapper function that takes a const char*
+// as parameter and passes it to printf and alike.
+//
+// The Microsoft Compiler/Linker is not working correctly with variadic
+// templates!
+//
+// Use the functions below or use std::cout (and similar)!
+//--------------------------------------------------------------------------------
+
+#if( 1 )
+static auto w_printf = [ ]( const char* fmt, ... ) {
+ va_list args;
+ va_start( args, fmt );
+ vprintf_s( fmt, args );
+ va_end( args );
+};
+
+static auto w_printf_s = [ ]( const char* fmt, ... ) {
+ va_list args;
+ va_start( args, fmt );
+ vprintf_s( fmt, args );
+ va_end( args );
+};
+
+static auto w_sprintf = [ ]( char* buf, const char* fmt, ... ) {
+ va_list args;
+ va_start( args, fmt );
+ vsprintf( buf, fmt, args );
+ va_end( args );
+};
+
+static auto w_sprintf_s = [ ]( char* buf, size_t buf_size, const char* fmt, ... ) {
+ va_list args;
+ va_start( args, fmt );
+ vsprintf_s( buf, buf_size, fmt, args );
+ va_end( args );
+};
+#endif
+
+//for compatibility with debug mode
+struct debug_ret {
+private:
+ const char* ret;
+
+public:
+ debug_ret( const char* str ) : ret( str ) { };
+
+ auto decrypt( ) {
+ return ret;
+ }
+};
+
+constexpr size_t strlen_ct( const char* const str ) {
+ size_t out = 1;
+
+ for( ; str[ out ] != '\0'; ++out );
+
+ return out;
+}
+
+#if 0
+#define xors_raw( s ) ( strenc::XorString< strenc::strlen_ct( s ), __COUNTER__ >( s, std::make_index_sequence< sizeof( s ) - 1>() ) )
+#define xors( s ) ( strenc::XorString< strenc::strlen_ct( s ), __COUNTER__ >( s, std::make_index_sequence< sizeof( s ) - 1>() ).decrypt() )
+#else
+#define xors_raw( s ) ( [ ]{ strenc::debug_ret ret{ s }; return ret; }( ) )
+#define xors( s ) ( s )
+#endif
+
+END_NAMESPACE \ No newline at end of file
diff --git a/client/util.hpp b/client/util.hpp
new file mode 100644
index 0000000..8e7d549
--- /dev/null
+++ b/client/util.hpp
@@ -0,0 +1,23 @@
+#pragma once
+#include <random>
+
+namespace util
+{
+ namespace {
+ //make a random generator and seed it with a p random number
+ static std::random_device rd;
+ static std::mt19937 gen( rd( ) );
+ }
+
+ template < typename t >
+ __forceinline t random_number( t min, t max ) {
+ if constexpr( !std::is_integral_v< t > ) {
+ std::uniform_real_distribution< t > dist( min, max );
+ return dist( gen );
+ }
+ else {
+ std::uniform_int_distribution< t > dist( min, max );
+ return dist( gen );
+ }
+ }
+} \ No newline at end of file