2

code first

DWORD WINAPI tcp_t(LPVOID lpParam)
{
    SOCKET tcp_client_s = (SOCKET)lpParam;
    struct sockaddr_in tcp_client;
    int tcp_client_len = sizeof(tcp_client), length;
    char req[4096], resp[4096];

    getpeername(tcp_client_s, (struct sockaddr *)&tcp_client, &tcp_client_len);

    cli_log(PROTO_TCP, LOG_SYS, "(%s:%d) TCP thread spawned\n", inet_ntoa(tcp_client.sin_addr), ntohs(tcp_client.sin_port));

    length = get_req_tcp(tcp_client_s, req, tcp_client);

    if(strci(req, "GET /syachi2ds/web/", 0))
    {
        while(!strstr(req, "Connection: close\r\n\r\n"))
            length += get_req_tcp(tcp_client_s, req + length, tcp_client);

        length = check_req(req, resp);

        if(length > 0)
            send_resp_tcp(tcp_client_s, resp, length, tcp_client);
    }

    closesocket(tcp_client_s);
    cli_log(PROTO_TCP, LOG_SYS, "(%s:%d) socket closed, closing thread\n", inet_ntoa(tcp_client.sin_addr), ntohs(tcp_client.sin_port));
    ExitThread(0);
}

int get_req_tcp(SOCKET in_s, char *buf, struct sockaddr_in in)
{
    int retval;

    cli_log(PROTO_TCP, LOG_COMM, "(%s:%d) waiting for incoming request...\n", inet_ntoa(in.sin_addr), ntohs(in.sin_port));
    if ( (retval = recv(in_s, buf, 4096, 0)) == SOCKET_ERROR)
        cli_log(PROTO_TCP, LOG_ERROR, "(%d) recv() failed\n", WSAGetLastError());
    cli_log(PROTO_TCP, LOG_COMM, "(%s:%d) data received\n", inet_ntoa(in.sin_addr), ntohs(in.sin_port));
    return retval;
}

This is part of a larger program I'm writing that does some kind of server emulation. It works good with TCP streams thar are not split into multiple packets, otherwise it gives winsock error 10014 on the first subsequent recv() invoked in the while loop.

PS: strci() is a custom case-insensitive strstr()

PS2: i know there's no check for buffer overflows on req array.

4

1 回答 1

5

10014 is WSAEFAULT, which means recv() is detecting that "the buf parameter is not completely contained in a valid part of the user address space.". That makes sense because you have a buffer overflow error in your code. You have allocated 4096 bytes on the call stack for your req buffer. Each time you call get_req_tcp(), you are telling it to read 4096 bytes, even if req does not actually have 4096 bytes available to read into.

Each time your loop runs, you are telling recv() to read bytes into a new start position within the buffer, but you are not telling recv() how many bytes are remaining after that position, so the loop overflows the buffer and will eventually access a memory address that is not on the call stack, causing the WSAEFAULT error.

You need to add an extra parameter to get_req_tcp() that tells it how many bytes to read.

Try this:

DWORD WINAPI tcp_t(LPVOID lpParam)
{
    SOCKET tcp_client_s = (SOCKET)lpParam;
    struct sockaddr_in tcp_client;
    int tcp_client_len = sizeof(tcp_client), length;
    char req[4096], resp[4096];

    getpeername(tcp_client_s, (struct sockaddr *)&tcp_client, &tcp_client_len);

    cli_log(PROTO_TCP, LOG_SYS, "(%s:%d) TCP thread spawned\n", inet_ntoa(tcp_client.sin_addr), ntohs(tcp_client.sin_port));

    length = get_req_tcp(tcp_client_s, req, sizeof(req), tcp_client);
    if (length > 0)
    {
        while (!strstr(req, "\r\n\r\n"))
        {
            retval = get_req_tcp(tcp_client_s, req + length, sizeof(req) - length, tcp_client);
            if (retval < 1)
            {
                length = 0;
                break;
            }
            length += retval;
        }

        if ((length > 0) && (strci(req, "GET /syachi2ds/web/", 0)))
        {
            length = check_req(req, resp);
            if (length > 0)
                send_resp_tcp(tcp_client_s, resp, length, tcp_client);
        }
    }

    closesocket(tcp_client_s);
    cli_log(PROTO_TCP, LOG_SYS, "(%s:%d) socket closed, closing thread\n", inet_ntoa(tcp_client.sin_addr), ntohs(tcp_client.sin_port));

    return 0;
}

int get_req_tcp(SOCKET in_s, char *buf, int buflen, struct sockaddr_in in)
{
    cli_log(PROTO_TCP, LOG_COMM, "(%s:%d) waiting for incoming request...\n", inet_ntoa(in.sin_addr), ntohs(in.sin_port));

    if ((!buf) || (buflen < 1))
    {
        cli_log(PROTO_TCP, LOG_ERROR, "invalid buffer passed for recv()\n");
        return -1;
    }

    int retval = recv(in_s, buf, buflen, 0);

    if (retval == SOCKET_ERROR)
    {
        cli_log(PROTO_TCP, LOG_ERROR, "(%d) recv() failed\n", WSAGetLastError());
        return -1;
    }

    if (retval == 0)
    {
        cli_log(PROTO_TCP, LOG_ERROR, "client disconnected\n");
        return 0;
    }

    cli_log(PROTO_TCP, LOG_COMM, "(%s:%d) %d bytes received\n", retval, inet_ntoa(in.sin_addr), ntohs(in.sin_port));

    return retval;
}
于 2013-01-02T20:29:51.837 回答