Search code examples
c++serverclientputtywinsock

C++ Winsock Library Termination When connecting with Putty Client on 127.0.0.1


I'm trying to create a server client that once its working I can pass a vector into it and send it to a client program through ssh like putty. The issue is whenever I try to connect raw or ssh with putty on 127.0.0.1:45000 the program terminates once it connects.

Here is my code:

#include <iostream>
#include <WS2tcpip.h>
#include <string>

#pragma comment (lib, "ws2_32.lib")

using namespace std;

void main()
{
    // Initialize winsock
    WSADATA wsData;
    WORD ver = MAKEWORD(2, 2);

    int wsOk = WSAStartup(ver, &wsData);
    if (wsOk != 0)
    {
        cerr << "Can't Intitialze winsock! Quiting" << endl;
        return;
    }

    // Create a socket to bind
    SOCKET listening = socket(AF_INET, SOCK_STREAM, 0);
    if (listening == INVALID_SOCKET)
    {
        cerr << "Can't create a socket! Quitting" << endl;
    }
    // Bind the socket to an ip address to the port
    sockaddr_in hint;
    hint.sin_family = AF_INET;
    hint.sin_port = htons(45000);
    hint.sin_addr.S_un.S_addr = INADDR_ANY; // could also use inet_pton

    bind(listening, (sockaddr*)&hint, sizeof(hint));

    // Tell winsock the socket is for listening
    listen(listening, SOMAXCONN);

    // Wait for connection
    sockaddr_in client;
    int clientSize = sizeof(client);

    SOCKET clientSocket = accept(listening, (sockaddr*)&client, &clientSize);

    char host[NI_MAXHOST];  //Clients remote name
    char service[NI_MAXHOST]; // Service (port) the client is on

    ZeroMemory(host, NI_MAXHOST);
    ZeroMemory(service, NI_MAXHOST); // use mem set of linux

    if (getnameinfo((sockaddr*)&client, sizeof(client), host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0)
    {
        cout << host << " connected on port " << service << endl;
        return;
    }
    else
    {
        inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
        cout << host << " connected on port " <<
            ntohs(client.sin_port) << endl;
        return;
    }

    // Close listening socket
    closesocket(listening);
    // while loop; accept and echo message back to client
    char buf[4096];
    while (true)
    {
        ZeroMemory(buf, 4096);

        // wait for client to send data
        int bytesReceived = recv(clientSocket, buf, 4096, 0);
        if (bytesReceived == SOCKET_ERROR)
        {
            cerr << "Error in recv(). Quitting" << endl;
            break;
        }
        if (bytesReceived == 0)
        {
            cout << "Client Disconnected, bytes 0" << endl;
            break;
        }
        // echo message back to client
        send(clientSocket, buf, bytesReceived + 1, 0);

        // Close the socket

    }
    closesocket(clientSocket);


    // Shutdown winsock
    WSACleanup();
}

I'm writing it and compiling in Visual Studio 2019.

Here's the message I get from Putty when trying to connect with the ssh option or raw.

Picture of Puttys error when trying to connect.

If anyone can help it would be greatly appreciated. Thanks!


Solution

  • When calling getnameinfo(), you are return'ing from main() immediately, without calling closesocket() first, regardless of whether getnameinfo() is successful or fails. This is the root of your Putty error. You are explicitly exiting your app whenever a client connects, without informing the client that the connection is being closed.

    More generally, if accept() is successful (and socket(), too), you should always call closesocket() on the returned SOCKET, regardless of anything else happening in your code (same with WSACleanup() if WSAStartup() is successful).

    There are several other mistakes in your code:

    • It is illegal for main() to have a non-int return type (though some compilers allow this, as a non-standard extension. Don't rely on this!).

    • You are missing a return from main() if socket() fails.

    • You are not checking for errors on bind(), listen(), accept(), or send().

    • There is no point in setting the backlog to SOMAXCONN if you are only going to accept() 1 client ever.

    • you have a potential buffer overflow when calling send(). Imagine if recv() returned exactly 4096 bytes received. Sending bytesReceived + 1 number of bytes back to the client would go out of bounds of your buf array.

    With that said, try something more like this:

    #include <iostream>
    #include <WS2tcpip.h>
    #include <string>
    
    #pragma comment (lib, "ws2_32.lib")
    
    using namespace std;
    
    int main()
    {
        // Initialize winsock
        WSADATA wsData;
        WORD ver = MAKEWORD(2, 2);
    
        int errCode = WSAStartup(ver, &wsData);
        if (errCode != 0)
        {
            cerr << "Can't initialize winsock! Error " << errCode << ". Quitting" << endl;
            return 0;
        }
    
        // Create a socket to bind
        SOCKET listening = socket(AF_INET, SOCK_STREAM, 0);
        if (listening == INVALID_SOCKET)
        {
            errCode = WSAGetLastError();
            cerr << "Can't create listening socket! Error " << errCode << ". Quitting" << endl;
            WSACleanup();
            return 0;
        }
    
        // Bind the socket to an ip address to the port
        sockaddr_in hint = {};
        hint.sin_family = AF_INET;
        hint.sin_port = htons(45000);
        hint.sin_addr.s_addr = INADDR_ANY; // could also use inet_pton
    
        if (bind(listening, (sockaddr*)&hint, sizeof(hint)) == SOCKET_ERROR)
        {
            errCode = WSAGetLastError();
            cerr << "Can't bind listening socket! Error " << errCode << ". Quitting" << endl;
            closesocket(listening);
            WSACleanup();
            return 0;
        }
    
        // Tell winsock the socket is for listening
        if (listen(listening, 1) == SOCKET_ERROR)
        {
            errCode = WSAGetLastError();
            cerr << "Can't open listening socket! Error " << errCode << ". Quitting" << endl;
            closesocket(listening);
            WSACleanup();
            return 0;
        }
    
        // Wait for connection
        sockaddr_in client;
        int clientSize = sizeof(client);
    
        SOCKET clientSocket = accept(listening, (sockaddr*)&client, &clientSize);
        if (clientSocket == INVALID_SOCKET)
        {
            errCode = WSAGetLastError();
            cerr << "Can't accept a client! Error " << errCode << ". Quitting" << endl;
            closesocket(listening);
            WSACleanup();
            return 0;
        }
    
        char host[NI_MAXHOST];  //Clients remote name
        char service[NI_MAXHOST]; // Service (port) the client is on
    
        ZeroMemory(host, NI_MAXHOST);
        ZeroMemory(service, NI_MAXHOST); // use mem set of linux
    
        if (getnameinfo((sockaddr*)&client, clientSize, host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0)
        {
            cout << host << " connected on port " << service << endl;
        }
        else
        {
            inet_ntop(AF_INET, &(client.sin_addr), host, NI_MAXHOST);
            cout << host << " connected on port " << ntohs(client.sin_port) << endl;
        }
    
        // Close listening socket
        closesocket(listening);
        listening = INVALID_SOCKET;
    
        // while loop; accept and echo message back to client
        char buf[4096];
        while (true)
        {
            // wait for client to send data
            int bytesReceived = recv(clientSocket, buf, sizeof(buf), 0);
            if (bytesReceived == SOCKET_ERROR)
            {
                errCode = WSAGetLastError();
                cerr << "Error reading from client: " << errCode << ". Quitting" << endl;
                break;
            }
    
            if (bytesReceived == 0)
            {
                cout << "Client Disconnected" << endl;
                break;
            }
    
            // echo message back to client
            char *ptr = buf;
            int bytesToSend = bytesReceived;
            do
            {
                int bytesSent = send(clientSocket, ptr, bytesToSend, 0);
                if (bytesSent == SOCKET_ERROR)
                    break;
                ptr += bytesSent;
                bytesToSend -= bytesSent;
            }
            while (bytesToSend > 0);
    
            if (bytesToSend != 0)
            {
                errCode = WSAGetLastError();
                cerr << "Error writing to client: " << errCode << ". Quitting" << endl;
                break;
            }
        }
    
        // Close the client socket
        closesocket(clientSocket);
    
        // Shutdown winsock
        WSACleanup();
    
        return 0;
    }