2

コードファースト

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;
}

これは、ある種のサーバー エミュレーションを行う、私が書いているより大きなプログラムの一部です。recv()複数のパケットに分割されていない TCP ストリームでうまく機能します。それ以外の場合は、while ループで最初に呼び出されたときに winsock エラー 10014 が発生します。

PS:strci()大文字と小文字を区別しないカスタムですstrstr()

PS2: req 配列のバッファ オーバーフローのチェックがないことはわかっています。

4

1 に答える 1

5

10014 はWSAEFAULT、「buf パラメータがユーザー アドレス空間の有効な部分に完全に含まれていない」ことを検出していることを意味recv()します。コードにバッファ オーバーフロー エラーがあるため、これは理にかなっています。reqバッファーの呼び出しスタックに 4096 バイトを割り当てました。を呼び出すたびに、実際には 4096 バイトを読み取ることができないget_req_tcp()場合でも、4096 バイトを読み取るように指示しています。req

ループが実行されるたびに、recv()バイトをバッファー内の新しい開始位置に読み取るように指示していますがrecv()、その位置の後に残っているバイト数を伝えていないため、ループはバッファーをオーバーフローし、最終的にはメモリ アドレスにアクセスします。コールスタックにないため、WSAEFAULTエラーが発生します。

get_req_tcp()読み取るバイト数を示す追加のパラメーターを追加する必要があります。

これを試して:

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 に答える