简体   繁体   English

C addrinfo结构损坏。 但是堆仍然有效

[英]C addrinfo struct gets corrupted. But heap is still valid

I try to create a socket and connect to a remote host. 我尝试创建一个套接字并连接到远程主机。 I resolve the remote host from domain with GetAddrInfo. 我使用GetAddrInfo从域中解析远程主机。 Which works fine. 哪个工作正常。 After the call i get a working addrinfo struct with the right values. 通话后,我得到一个具有正确值的工作addrinfo结构。 But in some situations the struct gets corrupted before calling connect(). 但是在某些情况下,该结构在调用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;
}

Where lookup_host is defined as: 其中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);

So this works well on my Quad-Core Win10 laptop. 因此,这在我的四核Win10笔记本电脑上效果很好。 But if i add for example a 但是如果我添加例如

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

call before the connect() call and examine the addrinfo struct in debugger it gets corrupted. 在connect()调用之前先调用,然后在调试器中检查addrinfo结构是否损坏。 For example some time i can see that the ai_canonname which should be "google.com" get overwritten with "After Sock". 例如,有时我可以看到应为“ google.com”的ai_canonname被“ After Sock”覆盖。 But the heap is still valid after that. 但是此后堆仍然有效。 So i have no idea where to start debugging this. 所以我不知道从哪里开始调试。 Could it be some other buffer or struct which overflows somewhere? 可能是某些其他缓冲区或结构在某处溢出吗?

your while (res) is moving the pointer res to other nodes other the origibal one each time you execute line res = res->ai_next; 每次执行行res = res->ai_next;您的while(res)将指针res移至origibal以外的其他节点res = res->ai_next; , but then you call FreeAddrInfo(res); ,但随后您调用FreeAddrInfo(res); at the end with modified pointer, not the one allocated via GetAddrInfo. 最后是经过修改的指针,而不是通过GetAddrInfo分配的指针。

Also, although you don't provide the start of the lookup_host function, but assuming an inout is ptr, you are setting it to the address of sin_addr from the allocated emory, and then freeing the memory at the end of the function, so it may be used by something else. 另外,尽管您没有提供lookup_host函数的开始,但是假设inout是ptr,您可以将其从分配的emory中设置为sin_addr的地址,然后在函数末尾释放内存,因此可能被其他东西使用。

It's because you free all the memory associated with the results before you use it. 这是因为您在使用结果之前先释放了与结果相关的所有内存。 For example ai_canonname is a pointer to a string that is allocated from the heap. 例如, ai_canonname是指向从堆分配的字符串的指针。 The memory containing its bytes is flagged as free for reuse before you exit lookup_host . 包含其字节的内存在退出lookup_host之前被标记为可重用。 Your CopyMemory will copy the pointer but not the bytes it points to. 您的CopyMemory将复制指针,但不复制其指向的字节。

NB you should post the whole of lookup_host including the function definition. 注意,您应该发布整个lookup_host包括函数定义。

You need to find a way of avoiding the call to FreeAddrInfo until after you are done. 您需要找到一种避免对FreeAddrInfo进行调用的方法,直到完成操作为止。 Either that or do a deeper copy of the struct including all the things being pointed to which you will find rapidly turns into a rabbit hole. 要么对该struct进行更深入的复制,要么对struct进行更深入的复制,包括所指向的所有内容,您很快就会发现它变成了兔子洞。

The way I would do it is to provide a callback function that is called while inside lookup_host 我这样做的方法是提供一个在lookup_host内部调用的回调函数

Your lookup_host() is not returning address data back to the caller correctly, not even close. 您的lookup_host()没有正确地将地址数据返回给调用者,甚至没有关闭。 addrinfo contains pointers to data outside of the addrinfo . addrinfo包含了外部数据指针addrinfo But you are just copying the addrinfo itself to the caller, but none of the data it points at. 但是,您只是将addrinfo本身复制到了调用方,但没有指向它的任何数据。

Try something more like this instead: 尝试类似这样的方法:

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

...

Note, however, that the above returns only the first address found. 但是请注意,以上仅返回找到的第一个地址。 A hostname can resolve to multiple IPs, and your PC might not have a route to all of them. 主机名可以解析为多个IP,并且您的PC可能没有通往所有IP的路由。 So, you should attempt to connect to every address reported until one is successful, eg: 因此,您应该尝试连接到所报告的每个地址,直到成功为止,例如:

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

...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM