Структура C addrinfo повреждена. Но куча все еще действует - PullRequest
0 голосов
/ 14 ноября 2018

Я пытаюсь создать сокет и подключиться к удаленному хосту. Я разрешаю удаленный хост из домена с помощью GetAddrInfo. Который работает отлично. После вызова я получаю рабочую структуру addrinfo с правильными значениями. Но в некоторых ситуациях структура повреждена перед вызовом connect ().

struct addrinfoW sa = { 0 };
ZeroMemory(&sa, sizeof(sa));
lookup_host(host, &sa);

int sock = 0;
sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock == -1) {
    return -1;
}
HeapValidate(GetProcessHeap(), HEAP_NO_SERIALIZE, NULL);
if(connect(sock, sa->ai_addr, sa->ai_addrlen) < 0) {
    HeapValidate(GetProcessHeap(), HEAP_NO_SERIALIZE, NULL);
#ifdef _DEBUG
    printf("Error: %d\n", GetLastError());
#endif // _DEBUG

    return -2;
}

Где lookup_host определяется как:

struct addrinfoW hints = { 0 };
struct addrinfoW *res;
int errcode;
ZeroMemory(&hints, sizeof(struct addrinfoW));
//ZeroMemory(res, sizeof(struct addrinfo));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags |= AI_CANONNAME;
errcode = GetAddrInfo(host, L"80", &hints, &res);//GetAddrInfoExW(L"google.de", L"80", NS_ALL, NULL, &hints, &res, NULL, NULL, NULL, NULL);  //GetAddrInfoEX(L"google.de", L"80", &hints, &res);
win_free(mbHost);
if (errcode != 0)
{
    //perror("getaddrinfo");
    return -1;
}
void *ptr = 0;
while (res)
{
    switch (res->ai_family)
    {
    case AF_INET:
        ptr = &((struct sockaddr_in *) res->ai_addr)->sin_addr;
        break;
    case AF_INET6:
        ptr = &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;
        break;
    }
    CopyMemory(out, res, sizeof(struct addrinfoW));
    break;
    res = res->ai_next;
}
FreeAddrInfo(res);

Так что это хорошо работает на моем ноутбуке Quad-Core Win10. Но если я добавлю, например,

MessageBoxA(NULL, "After sock", "HTTP", MB_ICONWARNING|MB_CANCELTRYCONTINUE | MB_DEFBUTTON2);

вызов перед вызовом connect () и проверка структуры addrinfo в отладчике, он поврежден. Например, какое-то время я вижу, что ai_canonname, которое должно быть "google.com", перезаписывается "After Sock". Но куча все еще действует после этого. Так что я понятия не имею, с чего начать отладку этого. Это может быть какой-то другой буфер или структура, которая где-то переполняется?

Ответы [ 3 ]

0 голосов
/ 14 ноября 2018

Это потому, что вы освобождаете всю память, связанную с результатами, прежде чем использовать ее. Например, ai_canonname - указатель на строку, выделенную из кучи. Память, содержащая ее байты, помечается как свободная для повторного использования перед выходом lookup_host. Ваш CopyMemory скопирует указатель, но не байты, на которые он указывает.

NB. Вы должны опубликовать все lookup_host, включая определение функции.

Вам нужно найти способ избежать звонка на FreeAddrInfo, пока вы не закончите. Либо это, либо сделайте более глубокую копию struct, включая все, на что вы указываете, что вы быстро найдете в кроличьей норе.

Я бы сделал это, предоставив функцию обратного вызова, которая вызывается внутри lookup_host

0 голосов
/ 15 ноября 2018

Ваш lookup_host() не возвращает адресные данные обратно вызывающей стороне правильно, даже близко не. addrinfo содержит указатели на данные вне addrinfo. Но вы просто копируете сам addrinfo в вызывающую сторону, но ни на одну из данных, на которые он указывает.

Попробуйте вместо этого что-нибудь еще:

int lookup_host(const wchar_t *host, int family, void *sa)
{
    struct addrinfoW hints;
    struct addrinfoW *res, *addr;
    int errcode, addrlen, returnval;
    void *ptr;

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = family;
    hints.ai_socktype = SOCK_STREAM;

    errcode = GetAddrInfoW(host, L"80", &hints, &res);
    //GetAddrInfoExW(host, L"80", NS_ALL, NULL, &hints, &res, NULL, NULL, NULL, NULL);
    if (errcode != 0)
    {
        //perror("getaddrinfo");
        return -1;
    }

    returnval = 0;

    for(addr = res; addr != NULL; addr = addr->ai_next)
    {
        switch (addr->ai_family)
        {
            case AF_INET:
                ptr = &((struct sockaddr_in *) addr->ai_addr)->sin_addr;
                addrlen = sizeof(struct sockaddr_in);
                break;

            case AF_INET6:
                ptr = &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;
                addrlen = sizeof(struct sockaddr_in6);
                break;

            default:
                continue;
        }

        CopyMemory(sa, ptr, addrlen);
        returnval = 1;
        break;
    }

    FreeAddrInfoW(res);

    return returnval;
}

...

struct sockaddr_in sa;

if (lookup_host(host, AF_INET, &sa) != 1) {
    return -1;
}

SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET) {
    return -1;
}

if (connect(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
    #ifdef _DEBUG
    printf("Error: %d\n", GetLastError());
    #endif // _DEBUG

    closesocket(sock);
    return -2;
}

...

Обратите внимание, что приведенное выше возвращает только первый найденный адрес. Имя хоста может преобразовываться в несколько IP-адресов, и на вашем компьютере может не быть маршрута ко всем из них. Таким образом, вы должны пытаться подключиться к каждому указанному адресу, пока он не будет успешным, например:

int lookup_host(const wchar_t *host, int family, struct addrinfoW **addrs)
{
    struct addrinfoW hints;
    int errcode;

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = family;
    hints.ai_socktype = SOCK_STREAM;

    errcode = GetAddrInfoW(host, L"80", &hints, addrs);
    //GetAddrInfoExW(host, L"80", NS_ALL, NULL, &hints, addrs, NULL, NULL, NULL, NULL);
    if (errcode != 0)
    {
        //perror("getaddrinfo");
        return -1;
    }

    return 0;
}

...

struct addrInfoW *res, *addr;

if (lookup_host(host, AF_INET, &res) < 0) { // or AF_INET6, or AF_UNSPEC
    return -1;
}

sock = INVALID_SOCKET;

for(addr = res; addr != NULL; addr = addr->ai_next)
{
    sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
    if (sock == INVALID_SOCKET) {
        break;
    }

    if (connect(sock, addr->ai_addr, addr->ai_addrlen) == 0) {
        break;
    }

    #ifdef _DEBUG
    printf("Error: %d\n", GetLastError());
    #endif // _DEBUG

    closesocket(sock);
    sock = INVALID_SOCKET;
}

FreeAddrInfoW(res);

if (sock == INVALID_SOCKET) {
    return -2;
}

...
0 голосов
/ 14 ноября 2018

ваш while (res) перемещает указатель res к другим узлам, отличным от исходного, каждый раз, когда вы выполняете строку res = res->ai_next;, но затем вы вызываете FreeAddrInfo(res); в конце с измененным указателем, а не с тем, который выделен через GetAddrInfo.

Кроме того, хотя вы не предоставляете начало функции lookup_host, но предполагаете, что inout равен ptr, вы устанавливаете его на адрес sin_addr из выделенного emory, а затем освобождаете память в конце функции так что это может быть использовано чем-то другим.

...