C addrinfo 结构损坏。但是堆仍然有效
C addrinfo struct gets corrupted. But heap is still valid
我尝试创建套接字并连接到远程主机。我使用 GetAddrInfo 从域解析远程主机。哪个工作正常。通话后,我得到一个具有正确值的工作地址信息结构。但在某些情况下,结构在调用 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);
所以这在我的四核 Win10 笔记本电脑上运行良好。但是如果我添加例如 a
MessageBoxA(NULL, "After sock", "HTTP", MB_ICONWARNING|MB_CANCELTRYCONTINUE | MB_DEFBUTTON2);
在 connect() 调用之前调用并检查调试器中的 addrinfo 结构是否已损坏。例如,有时我可以看到应该是 "google.com" 的 ai_canonname 被 "After Sock" 覆盖了。但是堆在那之后仍然有效。
所以我不知道从哪里开始调试这个。可能是其他缓冲区或结构在某处溢出了吗?
每次执行行 res = res->ai_next;
时,您的 while (res) 将指针 res
移动到原始节点以外的其他节点,但随后您在末尾调用 FreeAddrInfo(res);
修改指针,不是通过 GetAddrInfo 分配的指针。
此外,虽然你没有提供 lookup_host 函数的开始,但假设一个 inout 是 ptr,你将它设置为分配的 emory 中 sin_addr 的地址,并且然后在函数结束时释放内存,以便它可以被其他东西使用。
这是因为您在使用结果之前释放了与结果相关的所有内存。例如 ai_canonname
是指向从堆中分配的字符串的指针。在您退出 lookup_host
之前,包含其字节的内存被标记为可用以供重用。您的 CopyMemory
将复制指针而不是它指向的字节。
注意你应该 post 整个 lookup_host
包括函数定义。
你需要找到一种方法来避免调用 FreeAddrInfo
直到你完成。或者做一个更深层次的 struct
副本,包括所有被指向的东西,你会发现它们很快变成了一个兔子洞。
我的方法是提供一个在 lookup_host
内部调用的回调函数
您的 lookup_host()
没有将地址数据正确地返回给调用者,甚至没有关闭。 addrinfo
包含指向 addrinfo
之外的数据的指针。但是您只是将 addrinfo
本身复制给调用者,但是它指向的数据的 none。
尝试更像这样的东西:
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;
}
...
但是请注意,上面returns只是找到的第一个地址。一个主机名可以解析为多个 IP,您的 PC 可能没有到所有 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;
}
...
我尝试创建套接字并连接到远程主机。我使用 GetAddrInfo 从域解析远程主机。哪个工作正常。通话后,我得到一个具有正确值的工作地址信息结构。但在某些情况下,结构在调用 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);
所以这在我的四核 Win10 笔记本电脑上运行良好。但是如果我添加例如 a
MessageBoxA(NULL, "After sock", "HTTP", MB_ICONWARNING|MB_CANCELTRYCONTINUE | MB_DEFBUTTON2);
在 connect() 调用之前调用并检查调试器中的 addrinfo 结构是否已损坏。例如,有时我可以看到应该是 "google.com" 的 ai_canonname 被 "After Sock" 覆盖了。但是堆在那之后仍然有效。 所以我不知道从哪里开始调试这个。可能是其他缓冲区或结构在某处溢出了吗?
每次执行行 res = res->ai_next;
时,您的 while (res) 将指针 res
移动到原始节点以外的其他节点,但随后您在末尾调用 FreeAddrInfo(res);
修改指针,不是通过 GetAddrInfo 分配的指针。
此外,虽然你没有提供 lookup_host 函数的开始,但假设一个 inout 是 ptr,你将它设置为分配的 emory 中 sin_addr 的地址,并且然后在函数结束时释放内存,以便它可以被其他东西使用。
这是因为您在使用结果之前释放了与结果相关的所有内存。例如 ai_canonname
是指向从堆中分配的字符串的指针。在您退出 lookup_host
之前,包含其字节的内存被标记为可用以供重用。您的 CopyMemory
将复制指针而不是它指向的字节。
注意你应该 post 整个 lookup_host
包括函数定义。
你需要找到一种方法来避免调用 FreeAddrInfo
直到你完成。或者做一个更深层次的 struct
副本,包括所有被指向的东西,你会发现它们很快变成了一个兔子洞。
我的方法是提供一个在 lookup_host
您的 lookup_host()
没有将地址数据正确地返回给调用者,甚至没有关闭。 addrinfo
包含指向 addrinfo
之外的数据的指针。但是您只是将 addrinfo
本身复制给调用者,但是它指向的数据的 none。
尝试更像这样的东西:
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;
}
...
但是请注意,上面returns只是找到的第一个地址。一个主机名可以解析为多个 IP,您的 PC 可能没有到所有 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;
}
...