Giter Site home page Giter Site logo

eminfedar / async-sockets-cpp Goto Github PK

View Code? Open in Web Editor NEW
340.0 17.0 69.0 347 KB

Simple thread-based asynchronous TCP & UDP Socket classes in C++.

License: MIT License

C++ 97.03% Shell 2.97%
simple socket tcp udp cpp asynchronous-sockets thread

async-sockets-cpp's Introduction

Asynchronous Sockets for C++

Simple, multithread-based(not thread safe), non-blocking asynchronous Client-Server classes in C++ for TCP & UDP. Creates a thread for every connection. Use mutexes or atomic variables to provide thread-safe functions.

// Initialize a tcp socket.
TCPSocket tcpSocket;

// Connect to the host.
tcpSocket.Connect("127.0.0.1", 8888, [&] {
    cout << "Connected to the server successfully." << endl;

    // Send String:
    tcpSocket.Send("Hello Server!");
});

Super Easy!

CPU & RAM Usages (with single tcp connection & with single udp server + client): Lightweight

Lightweight!

Examples:

Examples You can compile all the examples by just going in the examples/ directory and run make in terminal:

Supported Platforms:

  • Linux
  • MacOS (not tested)

async-sockets-cpp's People

Contributors

eminfedar avatar friedrichengel avatar malcolmmielle avatar not-nik avatar stewpend0us avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

async-sockets-cpp's Issues

memory leak

$ valgrind -q --leak-check=full ./tcp-client
Connected to the server successfully.
Message from the Server: OK!
123
Message from the Server: OK!
123
Message from the Server: OK!
123
Message from the Server: OK!
123
Message from the Server: OK!
123
Message from the Server: OK!
123
Message from the Server: OK!
^C==9540== 288 bytes in 1 blocks are possibly lost in loss record 2 of 2
==9540==    at 0x483DD99: calloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==9540==    by 0x40149CA: allocate_dtv (dl-tls.c:286)
==9540==    by 0x40149CA: _dl_allocate_tls (dl-tls.c:532)
==9540==    by 0x4860322: allocate_stack (allocatestack.c:622)
==9540==    by 0x4860322: pthread_create@@GLIBC_2.2.5 (pthread_create.c:660)
==9540==    by 0x49510A8: std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==9540==    by 0x10CE6E: std::thread::thread<void (&)(TCPSocket*), TCPSocket*, void>(void (&)(TCPSocket*), TCPSocket*&&) (in /home/ardxwe/github/async-sockets-cpp/examples/tcp-client)
==9540==    by 0x10C788: TCPSocket::Listen() (in /home/ardxwe/github/async-sockets-cpp/examples/tcp-client)
==9540==    by 0x10C6F9: TCPSocket::Connect(unsigned int, unsigned short, std::function<void ()>, std::function<void (int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>) (in /home/ardxwe/github/async-sockets-cpp/examples/tcp-client)
==9540==    by 0x10C525: TCPSocket::Connect(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, unsigned short, std::function<void ()>, std::function<void (int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>) (in /home/ardxwe/github/async-sockets-cpp/examples/tcp-client)
==9540==    by 0x10A9AF: main (in /home/ardxwe/github/async-sockets-cpp/examples/tcp-client)
==9540==

I think we shouldn't use std::thread.detach.

Thread-based -> libuv's Event-Based transfering.

Rewrite the whole background of the code with libuv is my priority from now.

Threads are hard to manage, consuming RAM too much when client size increased.

After this, the library will be lightweight and fully single-threaded asynchronous in real.

Stack buffer overflow in static void Receive(TCPSocket* socket) at tcpsocket.hpp

Hi!

It appears that async-sockets-cpp contains a remote buffer overflow vulnerability in static void Receive(TCPSocket* socket) at tcpsocket.hpp, around lines 102-110. The buffer overflow affects all corresponding TCP servers. The remote buffer overflow can be triggered by connecting to a socket and sending a large buffer of bytes.

while ((messageLength = recv(socket->sock, tempBuffer, BUFFER_SIZE, 0)) > 0)
{
tempBuffer[messageLength] = '\0';
if(socket->onMessageReceived)
socket->onMessageReceived(std::string(tempBuffer, messageLength));
if(socket->onRawMessageReceived)
socket->onRawMessageReceived(tempBuffer, messageLength);
}

To confirm the issue, I first compiled the example tcp server from the async-sockets-cpp/examples folder with debug symbols and address sanitizer:

Makefile

CC		:= g++
CFLAGS	:= --std=c++11 -Wall -Wextra -Werror=conversion -fsanitize=address -g
LIBS	:= -lpthread -fsanitize=address
INC		:= ../async-sockets/include
RM		:= rm

.PHONY: all clean

all: tcp-client tcp-server udp-client udp-server

tcp-client: tcp-client.cpp $(INC)/tcpsocket.hpp
	$(CC) $(CFLAGS) $< -I$(INC) $(LIBS) -o $@

tcp-server: tcp-server.cpp $(INC)/tcpserver.hpp
	$(CC) $(CFLAGS) $< -I$(INC) $(LIBS) -o $@

udp-client: udp-client.cpp $(INC)/udpsocket.hpp
	$(CC) $(CFLAGS) $< -I$(INC) $(LIBS) -o $@

udp-server: udp-server.cpp $(INC)/udpserver.hpp
	$(CC) $(CFLAGS) $< -I$(INC) $(LIBS) -o $@

clean:
	$(RM) tcp-client
	$(RM) tcp-server
	$(RM) udp-client
	$(RM) udp-server

Compilation

$ make

Once the server was compiled, I executed the tcp-server on port 8888:

$ ./tcp-server

I then created a python3 script that will connect to the tcp-server and send a large packet with around 4096 (or larger) bytes of content:

import socket

host = "localhost"
port = 8888                   # The same port as used by the server
buf = b'A'*10000               # Overflow happens at 4095 bytes

try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))
    s.sendall(buf)
    data = s.recv(1024)
    s.close()
    print('Received', repr(data))
except:
    print("Completed...")

Executing the above python3 script will result in the server crashing and producing the following detailed output from address sanitizer showing the location of the stack buffer overflow:

ASAN Output

=================================================================
==1124507==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffff4ffdcb0 at pc 0x55555555ef97 bp 0x7ffff4ffcc00 sp 0x7ffff4ffcbf8
WRITE of size 1 at 0x7ffff4ffdcb0 thread T2
    #0 0x55555555ef96 in TCPSocket<(unsigned short)4096>::Receive(TCPSocket<(unsigned short)4096>*) ../async-sockets/include/tcpsocket.hpp:104
    #1 0x555555560775 in void std::__invoke_impl<void, void (*)(TCPSocket<(unsigned short)4096>*), TCPSocket<(unsigned short)4096>*>(std::__invoke_other, void (*&&)(TCPSocket<(unsigned short)4096>*), TCPSocket<(unsigned short)4096>*&&) /usr/include/c++/12/bits/invoke.h:61
    #2 0x5555555605eb in std::__invoke_result<void (*)(TCPSocket<(unsigned short)4096>*), TCPSocket<(unsigned short)4096>*>::type std::__invoke<void (*)(TCPSocket<(unsigned short)4096>*), TCPSocket<(unsigned short)4096>*>(void (*&&)(TCPSocket<(unsigned short)4096>*), TCPSocket<(unsigned short)4096>*&&) /usr/include/c++/12/bits/invoke.h:96
    #3 0x5555555604f2 in void std::thread::_Invoker<std::tuple<void (*)(TCPSocket<(unsigned short)4096>*), TCPSocket<(unsigned short)4096>*> >::_M_invoke<0ul, 1ul>(std::_Index_tuple<0ul, 1ul>) /usr/include/c++/12/bits/std_thread.h:252
    #4 0x55555556048f in std::thread::_Invoker<std::tuple<void (*)(TCPSocket<(unsigned short)4096>*), TCPSocket<(unsigned short)4096>*> >::operator()() /usr/include/c++/12/bits/std_thread.h:259
    #5 0x555555560453 in std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)(TCPSocket<(unsigned short)4096>*), TCPSocket<(unsigned short)4096>*> > >::_M_run() /usr/include/c++/12/bits/std_thread.h:210
    #6 0x7ffff74d44a2  (/lib/x86_64-linux-gnu/libstdc++.so.6+0xd44a2)
    #7 0x7ffff76a7fd3 in start_thread nptl/pthread_create.c:442
    #8 0x7ffff77285bb in clone3 ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

Address 0x7ffff4ffdcb0 is located in stack of thread T2 at offset 4224 in frame
    #0 0x55555555eea1 in TCPSocket<(unsigned short)4096>::Receive(TCPSocket<(unsigned short)4096>*) ../async-sockets/include/tcpsocket.hpp:97

  This frame has 3 object(s):
    [48, 49) '<unknown>'
    [64, 96) '<unknown>'
    [128, 4224) 'tempBuffer' (line 99) <== Memory access at offset 4224 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
Thread T2 created by T1 here:
    #0 0x7ffff7849726 in __interceptor_pthread_create ../../../../src/libsanitizer/asan/asan_interceptors.cpp:207
    #1 0x7ffff74d4578 in std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) (/lib/x86_64-linux-gnu/libstdc++.so.6+0xd4578)
    #2 0x55555555e36c in TCPSocket<(unsigned short)4096>::Listen() ../async-sockets/include/tcpsocket.hpp:87
    #3 0x55555555cf36 in TCPServer<(unsigned short)4096>::Accept(TCPServer<(unsigned short)4096>*, std::function<void (int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>) ../async-sockets/include/tcpserver.hpp:84
    #4 0x555555560909 in void std::__invoke_impl<void, void (*)(TCPServer<(unsigned short)4096>*, std::function<void (int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>), TCPServer<(unsigned short)4096>*, std::function<void (int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)> >(std::__invoke_other, void (*&&)(TCPServer<(unsigned short)4096>*, std::function<void (int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>), TCPServer<(unsigned short)4096>*&&, std::function<void (int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>&&) /usr/include/c++/12/bits/invoke.h:61
    #5 0x5555555606b5 in std::__invoke_result<void (*)(TCPServer<(unsigned short)4096>*, std::function<void (int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>), TCPServer<(unsigned short)4096>*, std::function<void (int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)> >::type std::__invoke<void (*)(TCPServer<(unsigned short)4096>*, std::function<void (int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>), TCPServer<(unsigned short)4096>*, std::function<void (int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)> >(void (*&&)(TCPServer<(unsigned short)4096>*, std::function<void (int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>), TCPServer<(unsigned short)4096>*&&, std::function<void (int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>&&) /usr/include/c++/12/bits/invoke.h:96
    #6 0x555555560558 in void std::thread::_Invoker<std::tuple<void (*)(TCPServer<(unsigned short)4096>*, std::function<void (int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>), TCPServer<(unsigned short)4096>*, std::function<void (int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)> > >::_M_invoke<0ul, 1ul, 2ul>(std::_Index_tuple<0ul, 1ul, 2ul>) /usr/include/c++/12/bits/std_thread.h:252
    #7 0x5555555604ab in std::thread::_Invoker<std::tuple<void (*)(TCPServer<(unsigned short)4096>*, std::function<void (int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>), TCPServer<(unsigned short)4096>*, std::function<void (int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)> > >::operator()() /usr/include/c++/12/bits/std_thread.h:259
    #8 0x555555560473 in std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)(TCPServer<(unsigned short)4096>*, std::function<void (int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>), TCPServer<(unsigned short)4096>*, std::function<void (int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)> > > >::_M_run() /usr/include/c++/12/bits/std_thread.h:210
    #9 0x7ffff74d44a2  (/lib/x86_64-linux-gnu/libstdc++.so.6+0xd44a2)

Thread T1 created by T0 here:
    #0 0x7ffff7849726 in __interceptor_pthread_create ../../../../src/libsanitizer/asan/asan_interceptors.cpp:207
    #1 0x7ffff74d4578 in std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) (/lib/x86_64-linux-gnu/libstdc++.so.6+0xd4578)
    #2 0x55555555beb3 in TCPServer<(unsigned short)4096>::Listen(std::function<void (int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>) ../async-sockets/include/tcpserver.hpp:56
    #3 0x55555555814d in main /home/kali/projects/fuzzing/async-sockets-cpp/examples/tcp-server.cpp:42
    #4 0x7ffff7646189 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58

SUMMARY: AddressSanitizer: stack-buffer-overflow ../async-sockets/include/tcpsocket.hpp:104 in TCPSocket<(unsigned short)4096>::Receive(TCPSocket<(unsigned short)4096>*)
Shadow bytes around the buggy address:
  0x10007e9f7b40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007e9f7b50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007e9f7b60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007e9f7b70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007e9f7b80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x10007e9f7b90: 00 00 00 00 00 00[f3]f3 f3 f3 f3 f3 f3 f3 f3 f3
  0x10007e9f7ba0: f3 f3 f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00
  0x10007e9f7bb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007e9f7bc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007e9f7bd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007e9f7be0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==1124507==ABORTING

A possible fix could be to check the size of messageLength before copying data to the buffer.

Thanks!

Installation under MacOS

Hello,

I am trying to install under MacOS.

The make command compiles and after I run sudo ./install.sh, the header files get copied as they should.

But, the .so file cannot be copied cause the target directory does not exist. Also I think it shouldn't be .so, cause Mac use dylib files.

memory leak in `TCPServer ::Accept` ?

I think that static void TCPServer ::Accept(TCPServer *server, FDR_ON_ERROR) has a memory leak:

            if (!server->isClosed && newSock >= 0)
            {
                TCPSocket *newSocket = new TCPSocket(onError, newSock);
                newSocket->setAddressStruct(newSocketInfo);

                server->onNewConnection(newSocket);
                newSocket->Listen();
            }

The problem is that newSocket is never deleted

Add QNX support

basesocket.hpp

#if defined(__linux__) || defined(__APPLE__) || defined(__QNX__)
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <netdb.h>
#elif _WIN32
#include <winsock32.h>
#endif

diffdefined(__QNX__)

TCP server stops receiving packets after around 160 seconds

Hi.
I'm using the tcp-server example.
after around 160 seconds the server stop receiving packets, without any warning or error.
when I rerun the program the server receives then packets again for 160 seconds and so on.

I'v checked it a few times and it happens in my code as well as in the example.

Installation under debian

Hello, is there a quick way to install in Debian so I can use globally on all my projects?
Thank you

program blocked when tcp client connection to a tcp server failed

Hi.
first of all - great work. the library is easy to use and understand.

When i try to connect to a tcp server that does not exist, the program get stuck after "Connection failed to the host" message is printed.
to reproduce that I ran the tcp-client.cpp example, I added print inside the while loop. It never gets printed.

while (input != "exit")
    {
        std::cout << "loop" << std::endl;
    
        tcpSocket.Send(input);
        getline(cin, input);
    }

Don't use strings for error notifications

The current implementation uses hard-coded strings for the error notifications, and they're not even accessible outside the scope.

This makes it's impossible to correctly capture all errors from the library without searching through the source for all the strings. It also makes comparison slower if you need to check for multiple error conditions compared to using something like an enum error code and a switch statement. Personally I'd propagate critical errors as exceptions in C++ but an error code would also be just fine.

TCP socket closed after 5 seconds

Hi.
I got weird behavior when using the library, specifically TCP socket.

Every time I open TCP client (TCPSocket) and connects to a TCP server, it (the client) closes after 5 seconds without activity, and then the onSocketClosed event is fired.
It happened when I tried to connect to other computer and also when I'm running the examples (tcp-client.cpp and tcp-server.cpp) on localhost (127.0.0.1).
When I place endless while loop with sending data to the server in it (and a small delay) inside the onConnected lambda function - the server prints the text I send and the socket stays open and not being closed automatically.

changing the value of the timeout (using Socket.setTimeout() function) does not affect the behavior.

edit:
I have noticed that this weird timeout is in the length of the timeout that defined in tcpsocket.h,
and using Socket.setTimeout() does not seems to change this value.

I'm using Ubuntu 18.04.
the /proc/sys/net/ipv4/tcp_keepalive_time set to the default - 7200,
the /proc/sys/net/ipv4/tcp_retries1 set to the default - 3,
and the /proc/sys/net/ipv4/tcp_retries2 also set to the default - 15

thanks!

"accept()" prevents TCPServer to close by blocking.

Even after the program died, the detached thread of TCPServer for listening the incoming sockets is still working at behind until it crashes at some point.

I can't send a signal to stop it because the thread is blocked by the accept function.

That is a huge problem.

tcpsocket in tcpserver question

Hi,
I will newClient variable assign outside variable of client , like it as below.
Next, I use command and telnet it, it is connected and fine.
Next, I close client via client variable, the status of IsClosed is 1 that it have closed this connection.
But my telnet is connection, and if I input any key in telnet then it will be closed.

My question is that newClient in the scope of onNewConnection then uses newClient->Close(), and my telnet is disconnected immediately.
I used client->Close() outside of the scope of onNewConnection, and client->IsCloseed showed as closed, but my telnet did not disconnect.

telnet 127.0.0.1 8888

    TCPSocket * client;
    tcpServer.onNewConnection = [&](TCPSocket *newClient) {
         client = newClient
    };

add func for tcpserver.hpp so that it can send msg to specific ip:port or send to all connected devices at once

New codes are added in between equal signs and the functionalities have been finely tested.

#pragma once

#include <map>
#include <thread>
#include <vector>

#include "tcpsocket.hpp"

using IP_MAP = std::map<std::pair<std::string, int>, int>;


template <uint16_t BUFFER_SIZE = AS_DEFAULT_BUFFER_SIZE>
class TCPServer : public BaseSocket
{
public:
    // Event Listeners:
    std::function<void(TCPSocket<BUFFER_SIZE>*)> onNewConnection = [](TCPSocket<BUFFER_SIZE>* sock){FDR_UNUSED(sock)};

    explicit TCPServer(FDR_ON_ERROR): BaseSocket(onError, SocketType::TCP)
    {
        int opt = 1;
        setsockopt(this->sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
        setsockopt(this->sock, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(int));
    }

    // =================================================================
    // Send raw bytes
    ssize_t Send(std::string ip, int port, const char* bytes, size_t byteslength)
    {
        int ns = ipPortMap[std::make_pair(ip, port)];
        return send(ns, bytes, byteslength, 0);
    }
    // Send std::string
    ssize_t Send(std::string ip, int port, const std::string& message)
    {
        return this->Send(ip, port, message.c_str(), message.length());
    }

    // Send raw bytes
    bool SendAll(const char* bytes, size_t byteslength)
    {
        for (IP_MAP::iterator it = ipPortMap.begin(); it != ipPortMap.end(); it++) {
            send(it->second, bytes, byteslength, 0);
            // std::cout << it->second << std::endl;
        }
        return true;
    }
    // Send std::string
    bool SendAll(const std::string& message)
    {
        return this->SendAll(message.c_str(), message.length());
    }
    // =================================================================

    // Bind the custom address & port of the server.
    void Bind(const char* address, uint16_t port, FDR_ON_ERROR)
    {
        int status = inet_pton(AF_INET, address, &this->address.sin_addr);
        switch (status) {
            case -1:
                onError(errno, "Invalid address. Address type not supported.");
                return;
            case 0:
                onError(errno, "AF_INET is not supported. Please send message to developer.");
                return;
            default:
                break;
        }

        this->address.sin_family = AF_INET;
        this->address.sin_port = htons(port);

        if (bind(this->sock, (const sockaddr*)&this->address, sizeof(this->address)) == -1)
        {
            onError(errno, "Cannot bind the socket.");
            return;
        }
    }
    // Bind the address(0.0.0.0) & port of the server.
    void Bind(uint16_t port, FDR_ON_ERROR) { this->Bind("0.0.0.0", port, onError); }

    // Start listening incoming connections.
    void Listen(FDR_ON_ERROR)
    {
        if (listen(this->sock, 20) == -1)
        {
            onError(errno, "Error: Server can't listen the socket.");
            return;
        }

        // =============================================================
        // Both ways works
        // std::thread t(Accept, this, std::ref(TCPServer::ipPortMap), onError);
        std::thread t(Accept, this, std::ref(this->ipPortMap), onError);
        // =============================================================
        t.detach();
    }

private:
    // =================================================================
    IP_MAP ipPortMap;
    // =================================================================

    static void Accept(TCPServer<BUFFER_SIZE>* server, IP_MAP& ipm, FDR_ON_ERROR)
    {
        sockaddr_in newSocketInfo;
        socklen_t newSocketInfoLength = sizeof(newSocketInfo);

        int newSocketFileDescriptor = -1;
        while (true)
        {
            newSocketFileDescriptor = accept(server->sock, (sockaddr*)&newSocketInfo, &newSocketInfoLength);
            if (newSocketFileDescriptor == -1)
            {
                if (errno == EBADF || errno == EINVAL) {
                    return;
                }

                onError(errno, "Error while accepting a new connection.");
                return;
            }

            TCPSocket<BUFFER_SIZE>* newSocket = new TCPSocket<BUFFER_SIZE>(onError, newSocketFileDescriptor);
            newSocket->deleteAfterClosed = true;
            newSocket->setAddressStruct(newSocketInfo);

            server->onNewConnection(newSocket);
            newSocket->Listen();

            // =========================================================
            std::pair<std::string, int> p = std::make_pair(newSocket->remoteAddress(),
                                                           newSocket->remotePort());
            ipm[p] = newSocketFileDescriptor;
            // std::cout << p.first << ":" << p.second << std::endl;
            // =========================================================
        }
    }
};

Not accepting connections

Hello again,

for some reason, while running the example tcp server I cannot connect to the server.

It reaches the exit loop without errors, but while trying to connect I get errors that the server isn't available.

non-static const is causing problems

in basesocket.hpp line 29:
const uint16_t BUFFER_SIZE = 0xFFFF;

please make it:
static const uint16_t BUFFER_SIZE = 0xFFFF;

otherwise the function 'TCPSocket& TCPSocket::operator=(TCPSocket&&)' is deleted by the compiler and I can't do
mysocket = TCPSocket();

just changing that one constant to be static fixes the problem for me and works great

client crashed when server is closed

Hi and thanks again for this great library.
i'm in the latest commit and noticed that when the server program is closed.

image

this wasn't happening in earlier versions.

Stack Buffer Overflow in static void ReceiveFrom(UDPSocket* udpSocket) at udpsocket.hpp

Hi!

It appears that async-sockets-cpp (through 0.3.1) contains a remote buffer overflow vulnerability in static void ReceiveFrom(UDPSocket* udpSocket) at udpsocket.hpp, around lines 160-167. The buffer overflow affects all corresponding UDP servers. The remote buffer overflow can be triggered by connecting to a UDP socket and sending a large buffer of bytes (similar to it's TCP counterpart ).

while ((messageLength = recvfrom(udpSocket->sock, tempBuffer, BUFFER_SIZE, 0, (sockaddr* )&hostAddr, &hostAddrSize)) != -1)
{
tempBuffer[messageLength] = '\0';
if (udpSocket->onMessageReceived)
udpSocket->onMessageReceived(std::string(tempBuffer, messageLength), ipToString(hostAddr), ntohs(hostAddr.sin_port));
if (udpSocket->onRawMessageReceived)
udpSocket->onRawMessageReceived(tempBuffer, messageLength, ipToString(hostAddr), ntohs(hostAddr.sin_port));

To confirm the issue, I first compiled the example UDP server from the async-sockets-cpp/examples folder with debug symbols and address sanitizer:

Makefile

CC		:= g++
CFLAGS	:= --std=c++11 -Wall -Wextra -Werror=conversion -fsanitize=address -g
LIBS	:= -lpthread -fsanitize=address
INC		:= ../async-sockets/include
RM		:= rm

.PHONY: all clean

all: tcp-client tcp-server udp-client udp-server

tcp-client: tcp-client.cpp $(INC)/tcpsocket.hpp
	$(CC) $(CFLAGS) $< -I$(INC) $(LIBS) -o $@

tcp-server: tcp-server.cpp $(INC)/tcpserver.hpp
	$(CC) $(CFLAGS) $< -I$(INC) $(LIBS) -o $@

udp-client: udp-client.cpp $(INC)/udpsocket.hpp
	$(CC) $(CFLAGS) $< -I$(INC) $(LIBS) -o $@

udp-server: udp-server.cpp $(INC)/udpserver.hpp
	$(CC) $(CFLAGS) $< -I$(INC) $(LIBS) -o $@

clean:
	$(RM) tcp-client
	$(RM) tcp-server
	$(RM) udp-client
	$(RM) udp-server

Compilation

$ make

Once the server was compiled, I executed the udp-server on port 8888:

$ ./udp-server

I then created a python3 script to connect to the udp-server and send a large packet with around 4096 (or larger) bytes of content:

import socket

host = "localhost"
port = 8888                   # The same port as used by the server
buf = b'A'*10000               # Overflow happens at 4095 bytes

try:
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect((host, port))
    s.sendall(buf)
    data = s.recv(1024)
    s.close()
    print('Received', repr(data))
except:
    print("Completed...")

Executing the above python3 script will result in the server/thread crashing and producing the following detailed output from address sanitizer showing the location of the stack buffer overflow:

ASAN Output

=================================================================
==352142==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7f613a200120 at pc 0x55b71fc37288 bp 0x7f613adfdba0 sp 0x7f613adfdb98
WRITE of size 1 at 0x7f613a200120 thread T1
    #0 0x55b71fc37287 in UDPSocket<(unsigned short)4096>::ReceiveFrom(UDPSocket<(unsigned short)4096>*) ../async-sockets/include/udpsocket.hpp:162
    #1 0x55b71fc3a309 in void std::__invoke_impl<void, void (*)(UDPSocket<(unsigned short)4096>*), UDPSocket<(unsigned short)4096>*>(std::__invoke_other, void (*&&)(UDPSocket<(unsigned short)4096>*), UDPSocket<(unsigned short)4096>*&&) /usr/include/c++/12/bits/invoke.h:61
    #2 0x55b71fc3a24c in std::__invoke_result<void (*)(UDPSocket<(unsigned short)4096>*), UDPSocket<(unsigned short)4096>*>::type std::__invoke<void (*)(UDPSocket<(unsigned short)4096>*), UDPSocket<(unsigned short)4096>*>(void (*&&)(UDPSocket<(unsigned short)4096>*), UDPSocket<(unsigned short)4096>*&&) /usr/include/c++/12/bits/invoke.h:96
    #3 0x55b71fc3a1bc in void std::thread::_Invoker<std::tuple<void (*)(UDPSocket<(unsigned short)4096>*), UDPSocket<(unsigned short)4096>*> >::_M_invoke<0ul, 1ul>(std::_Index_tuple<0ul, 1ul>) /usr/include/c++/12/bits/std_thread.h:279
    #4 0x55b71fc3a175 in std::thread::_Invoker<std::tuple<void (*)(UDPSocket<(unsigned short)4096>*), UDPSocket<(unsigned short)4096>*> >::operator()() /usr/include/c++/12/bits/std_thread.h:286
    #5 0x55b71fc3a159 in std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)(UDPSocket<(unsigned short)4096>*), UDPSocket<(unsigned short)4096>*> > >::_M_run() /usr/include/c++/12/bits/std_thread.h:231
    #6 0x7f613dedc482  (/lib/x86_64-linux-gnu/libstdc++.so.6+0xdc482) (BuildId: 8ce98760d7c54203c5d99ee3bdd9fbca8e275529)
    #7 0x7f613dca63eb in start_thread nptl/pthread_create.c:444
    #8 0x7f613dd26a1b in clone3 ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

Address 0x7f613a200120 is located in stack of thread T1 at offset 4384 in frame
    #0 0x55b71fc3712e in UDPSocket<(unsigned short)4096>::ReceiveFrom(UDPSocket<(unsigned short)4096>*) ../async-sockets/include/udpsocket.hpp:152

  This frame has 7 object(s):
    [32, 33) '<unknown>'
    [48, 52) 'hostAddrSize' (line 155)
    [64, 80) 'hostAddr' (line 154)
    [96, 128) '<unknown>'
    [160, 192) '<unknown>'
    [224, 256) '<unknown>'
    [288, 4384) 'tempBuffer' (line 157) <== Memory access at offset 4384 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
Thread T1 created by T0 here:
    #0 0x7f613e247c36 in __interceptor_pthread_create ../../../../src/libsanitizer/asan/asan_interceptors.cpp:208
    #1 0x7f613dedc558 in std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) (/lib/x86_64-linux-gnu/libstdc++.so.6+0xdc558) (BuildId: 8ce98760d7c54203c5d99ee3bdd9fbca8e275529)
    #2 0x55b71fc35e83 in UDPSocket<(unsigned short)4096>::UDPSocket(bool, std::function<void (int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)>, int) ../async-sockets/include/udpsocket.hpp:23
    #3 0x55b71fc35792 in UDPServer<(unsigned short)4096>::UDPServer() ../async-sockets/include/udpserver.hpp:7
    #4 0x55b71fc338c6 in main /home/kali/projects/fuzzing/async-sockets-cpp/examples/udp-server.cpp:9
    #5 0x7f613dc456c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58

SUMMARY: AddressSanitizer: stack-buffer-overflow ../async-sockets/include/udpsocket.hpp:162 in UDPSocket<(unsigned short)4096>::ReceiveFrom(UDPSocket<(unsigned short)4096>*)
Shadow bytes around the buggy address:
  0x7f613a1ffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7f613a1fff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7f613a1fff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7f613a200000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7f613a200080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x7f613a200100: 00 00 00 00[f3]f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3
  0x7f613a200180: f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
  0x7f613a200200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7f613a200280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7f613a200300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7f613a200380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==352142==ABORTING


Similar to #31 (comment), a possible fix could be to check the size of messageLength before copying data to the buffer.

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.