c++tcplistenhoneypot

How can I make my TCP listening app safer?


I’m looking for advice on writing a very safe C/C++ app that will listen to a tcp port. I'd like to peek at what others are trying to do to my machine, but I’d rather not install an elaborate honeypot app if I can do what I need with a very short program. With my goal being simple and safe, I want to make sure I handle at least the following:
1) The client should not be able to send an unlimited amount of data,
2) The client(s) should not be able to overload the server with too many connections and
3) The client should not be able to keep a connection open indefinitely.
Because my current app is small and functional, a third party library doesn't seem warranted for this project. However, a small library that would simplify the app further would be interesting. Also, I expect the C++ standard library may help in simplifying my code.

Below are some key functions from my initial attempt. This code compiles on Windows, OSX and Linux.

How can I make the app safer, or simpler?

void SafeClose(SOCKET s)
{
    if (s == INVALID_SOCKET)
        return;
    shutdown(s, SHUT_RDWR);
    closesocket(s);
}

void ProcessConnection(SOCKET sock, sockaddr_in *sockAddr)
{
    time_t time1;
    char szTime[TIME_BUF_SIZE];
    char readBuf[READ_BUF_SIZE];

    time(&time1);
    strftime(szTime, TIME_BUF_SIZE-1, "%Y/%m/%d %H:%M:%S", localtime(&time1)); 
    printf("%s - %s\n", szTime, inet_ntoa(sockAddr->sin_addr));
    usleep(1000000);       // Wait 1 second for client to send something
    int actualReadCount = recv(sock, readBuf, READ_BUF_SIZE-1, 0); 

    if (actualReadCount < 0 || actualReadCount >= READ_BUF_SIZE){
        actualReadCount = 0;
        strcpy(readBuf, "(Nothing)");
    } else {
        CleanString(readBuf, actualReadCount); // Replace non-printable characters
        readBuf[actualReadCount] = 0;
    }

    printf("%s\n\n", readBuf);
    SafeClose(sock);
}


int main(int argc, char* argv[])
{
    int             port            = 80;
    char            cmd[CMD_BUF_SIZE]   = {0};
    sockaddr_in     newSockAddr;
    sockaddr_in     localSockAddr;

    if(argc < 2){
        printf("Usage: safelisten PORT\n\n");
        return error_code;
    }

    error_code++;
    port = atoi(argv[1]); 
    BuildCleanAsciiMap(); // Used to replace non-printable characters

    localSockAddr.sin_family        = AF_INET;              
    localSockAddr.sin_addr.s_addr   = INADDR_ANY;           
    localSockAddr.sin_port          = htons(port);          
    sizeNewSockAddr                 = sizeof newSockAddr;

    CHK( listenSocket = socket(AF_INET, SOCK_STREAM, 0));
    CHK( setsockopt(listenSocket, SOL_SOCKET,  SO_REUSEADDR, (char *)&on, sizeof(on)));
    CHK( bind(listenSocket, (sockaddr*)&localSockAddr, sizeof localSockAddr));
    CHK( listen(listenSocket, BACKLOG));
    CHK( SetNonBlocking(listenSocket));
    CHK( SetNonBlocking(0));        // Set STDIN to nonblocking for linux    
    printf ("Listening on port: %d\nEnter q to quit.\n\n", port);

    while(strcmp(cmd, "q")) // While the user has not entered q ...
    {
        newSocket = accept(listenSocket, (sockaddr*)&newSockAddr, &sizeNewSockAddr);
        ReadCmd(cmd, CMD_BUF_SIZE);

        if(newSocket == INVALID_SOCKET) { 
            // accept() would have blocked, thus we wait and try again
            usleep(10000);
            continue;   
        }

        // Set to nonblocking because we don't want the client to dictate how 
        // long we are connected.
        CHK( SetNonBlocking(newSocket)); 
        ProcessConnection(newSocket, &newSockAddr);
    }

    SafeClose(listenSocket);
    return 0;
}

Solution

  • 2) The client(s) should not be able to overload the server with too many connections...

    Your best options is letting the router limit how many connections an IP address makes, as, by the time your program gets the connection info there are system resources already allocated.

    1) The client should not be able to send an unlimited amount of data,

    The best option is to have some reasonable max that can be sent, and just keep track of how much you have read, and once you reach the max limit, close the connection.

    3) The client should not be able to keep a connection open indefinitely.

    For this, you can just create a thread that will start a countdown, so when a connection has been open for a specific amount of time, it can timeout and you can just close the connection.

    There are other, more complicated, solutions, but I think this would be a good starting point.

    If you want to do something about (2), keep a hashmap in memory and just increment how many connections have been made, but you may also want to keep track of time. You want the data structure to be very fast, so you can decide if you need to disconnect the connection early.

    For example, if you had two longs in the hashmap, then you could put the time (in unixtime or ticks) of the first connection, and increment, and if you get 10 connections in less than one minute, start to close the excess until a minute has passed. Then remove that element if it has had connections long enough that you trust it was not attacking you.

    Also, make certain you validate all the parameters being passed to you, and have reasonable max values for any strings.