愿意是模仿trace route,利用原始套接字,发送一个ICMP回显请求,随着TTL递增,获得途经路由。
但是呢,程序一直不能recvfrom到icmp差错报文,照常是会收到超时类型的差错报文的,但却始终收不到。
程序是已管理员身份运行的。
而程序中回显应答确实可以被正常recvfrom到。
也尝试用了wireshark在本地抓包,发现是有ICMP差错报文反馈回来的,并不是远程路由等的问题。
很头疼,网上也查不到类似错误,希望大家能指点一二。void ConnectToHost(char* strHost)
{
m_sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (m_sockRaw == INVALID_SOCKET)
{
cerr << "WSASocket() failed " << WSAGetLastError() << endl;
ExitProcess(1);
} //绑定
SOCKADDR_IN localaddr;
ZeroMemory(&localaddr, sizeof(SOCKADDR_IN));
localaddr.sin_family = AF_INET;
localaddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
bind(m_sockRaw, (sockaddr*)&localaddr, sizeof(SOCKADDR_IN)); //设置套接字接收超时选项
int ret = setsockopt(m_sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&m_nTimeout, sizeof(m_nTimeout));
if (ret == SOCKET_ERROR)
{
cerr << "setsockopt(SO_RCVTIMEO) failed " << WSAGetLastError() << endl;
return;
} //设置套接字发送超时选项
m_nTimeout = 10000;
ret = setsockopt(m_sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char*)&m_nTimeout, sizeof(m_nTimeout));
if (ret == SOCKET_ERROR)
{
cerr << "setsockopt(SO_SNDTIMEO) failed " << WSAGetLastError() << endl;
return;
} //填充目的地址
ZeroMemory(&m_addrDest, sizeof(m_addrDest));
m_addrDest.sin_family = AF_INET;
if ((m_addrDest.sin_addr.S_un.S_addr = inet_addr(strHost)) == INADDR_NONE)
{
hostent *host = NULL;
host = gethostbyname(strHost);
if (host != NULL)
{
CopyMemory(&(m_addrDest.sin_addr), host->h_addr_list[0], host->h_length);
}
else
{
cerr << "gethostbyname() failed " << WSAGetLastError() << endl;
return;
}
} //设置不路由选项
bool bopt = true;
if (setsockopt(m_sockRaw, SOL_SOCKET, SO_DONTROUTE, (char*)&bopt, sizeof(bool)) == SOCKET_ERROR)
{
cerr << "setsockopt(SO_DONTROUTE) failed " << WSAGetLastError() << endl;
return;
} //为发送缓冲区和接收缓冲区申请内存,这里确保是足够的。
m_nDatasize = DEF_PACKET_SIZE;
m_nDatasize += sizeof(IcmpHeader);
m_ICMPData = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PACKET);
m_RecvBuf = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PACKET);
if (!m_ICMPData || !m_RecvBuf)
{
cerr << "HeapAlloc() failed " << endl;
return;
} //往发送缓冲区中填充ICMP数据
ZeroMemory(m_ICMPData, MAX_PACKET);
FillICMPData(m_ICMPData, m_nDatasize);
string str;
if (inet_addr(strHost) == INADDR_NONE)
{
str = strHost;
str += " [";
str += inet_ntoa(m_addrDest.sin_addr);
str += "]";
}
else
str = strHost;
cout << "通过最多 " << m_nMaxhops << " 个跃点跟踪到 " << str.c_str() << " 的路由:" << endl;
//递增ttl发送ICMP回显请求
for (m_nTTL = 1; m_nTTL <= m_nMaxhops && !m_bDone; ++m_nTTL)
{
int ret;
SetTTL(m_sockRaw, m_nTTL); ((IcmpHeader*)m_ICMPData)->checksum = 0;
((IcmpHeader*)m_ICMPData)->seq = m_Seqno++;
m_dwCurrentTick = GetTickCount();
((IcmpHeader*)m_ICMPData)->timestamp = m_dwCurrentTick;
((IcmpHeader*)m_ICMPData)->checksum = checksum((unsigned short*)m_ICMPData, m_nDatasize);
ret = sendto(m_sockRaw, m_ICMPData, m_nDatasize, 0, (SOCKADDR*)&m_addrDest, sizeof(SOCKADDR_IN));
if (ret == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
cout << "发送超时" << endl;
continue;
}
else
{
cerr << "sendto() failed " << WSAGetLastError()<< endl;
return;
}
}
int fromlen = sizeof(SOCKADDR_IN);
ret = recvfrom(m_sockRaw, m_RecvBuf, MAX_PACKET, 0, (SOCKADDR*)&m_addrFrom, &fromlen);
if (ret == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
cout << "接收超时" << endl;
continue;
}
else
{
cerr << "recvfrom() failed " << WSAGetLastError() << endl;
return;
}
}
//解析接收到的ICMP差错报文或者回显应答报文
m_bDone = DecodeRecv(m_RecvBuf, ret, &m_addrFrom, m_nTTL);
Sleep(1000);
}
}
但是呢,程序一直不能recvfrom到icmp差错报文,照常是会收到超时类型的差错报文的,但却始终收不到。
程序是已管理员身份运行的。
而程序中回显应答确实可以被正常recvfrom到。
也尝试用了wireshark在本地抓包,发现是有ICMP差错报文反馈回来的,并不是远程路由等的问题。
很头疼,网上也查不到类似错误,希望大家能指点一二。void ConnectToHost(char* strHost)
{
m_sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (m_sockRaw == INVALID_SOCKET)
{
cerr << "WSASocket() failed " << WSAGetLastError() << endl;
ExitProcess(1);
} //绑定
SOCKADDR_IN localaddr;
ZeroMemory(&localaddr, sizeof(SOCKADDR_IN));
localaddr.sin_family = AF_INET;
localaddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
bind(m_sockRaw, (sockaddr*)&localaddr, sizeof(SOCKADDR_IN)); //设置套接字接收超时选项
int ret = setsockopt(m_sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&m_nTimeout, sizeof(m_nTimeout));
if (ret == SOCKET_ERROR)
{
cerr << "setsockopt(SO_RCVTIMEO) failed " << WSAGetLastError() << endl;
return;
} //设置套接字发送超时选项
m_nTimeout = 10000;
ret = setsockopt(m_sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char*)&m_nTimeout, sizeof(m_nTimeout));
if (ret == SOCKET_ERROR)
{
cerr << "setsockopt(SO_SNDTIMEO) failed " << WSAGetLastError() << endl;
return;
} //填充目的地址
ZeroMemory(&m_addrDest, sizeof(m_addrDest));
m_addrDest.sin_family = AF_INET;
if ((m_addrDest.sin_addr.S_un.S_addr = inet_addr(strHost)) == INADDR_NONE)
{
hostent *host = NULL;
host = gethostbyname(strHost);
if (host != NULL)
{
CopyMemory(&(m_addrDest.sin_addr), host->h_addr_list[0], host->h_length);
}
else
{
cerr << "gethostbyname() failed " << WSAGetLastError() << endl;
return;
}
} //设置不路由选项
bool bopt = true;
if (setsockopt(m_sockRaw, SOL_SOCKET, SO_DONTROUTE, (char*)&bopt, sizeof(bool)) == SOCKET_ERROR)
{
cerr << "setsockopt(SO_DONTROUTE) failed " << WSAGetLastError() << endl;
return;
} //为发送缓冲区和接收缓冲区申请内存,这里确保是足够的。
m_nDatasize = DEF_PACKET_SIZE;
m_nDatasize += sizeof(IcmpHeader);
m_ICMPData = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PACKET);
m_RecvBuf = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PACKET);
if (!m_ICMPData || !m_RecvBuf)
{
cerr << "HeapAlloc() failed " << endl;
return;
} //往发送缓冲区中填充ICMP数据
ZeroMemory(m_ICMPData, MAX_PACKET);
FillICMPData(m_ICMPData, m_nDatasize);
string str;
if (inet_addr(strHost) == INADDR_NONE)
{
str = strHost;
str += " [";
str += inet_ntoa(m_addrDest.sin_addr);
str += "]";
}
else
str = strHost;
cout << "通过最多 " << m_nMaxhops << " 个跃点跟踪到 " << str.c_str() << " 的路由:" << endl;
//递增ttl发送ICMP回显请求
for (m_nTTL = 1; m_nTTL <= m_nMaxhops && !m_bDone; ++m_nTTL)
{
int ret;
SetTTL(m_sockRaw, m_nTTL); ((IcmpHeader*)m_ICMPData)->checksum = 0;
((IcmpHeader*)m_ICMPData)->seq = m_Seqno++;
m_dwCurrentTick = GetTickCount();
((IcmpHeader*)m_ICMPData)->timestamp = m_dwCurrentTick;
((IcmpHeader*)m_ICMPData)->checksum = checksum((unsigned short*)m_ICMPData, m_nDatasize);
ret = sendto(m_sockRaw, m_ICMPData, m_nDatasize, 0, (SOCKADDR*)&m_addrDest, sizeof(SOCKADDR_IN));
if (ret == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
cout << "发送超时" << endl;
continue;
}
else
{
cerr << "sendto() failed " << WSAGetLastError()<< endl;
return;
}
}
int fromlen = sizeof(SOCKADDR_IN);
ret = recvfrom(m_sockRaw, m_RecvBuf, MAX_PACKET, 0, (SOCKADDR*)&m_addrFrom, &fromlen);
if (ret == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
cout << "接收超时" << endl;
continue;
}
else
{
cerr << "recvfrom() failed " << WSAGetLastError() << endl;
return;
}
}
//解析接收到的ICMP差错报文或者回显应答报文
m_bDone = DecodeRecv(m_RecvBuf, ret, &m_addrFrom, m_nTTL);
Sleep(1000);
}
}
解决方案 »
- WinXP: Win32MFC程序,VS2005 CEdit不能多行显示问题
- VS2008从txt文件序列化到CString后输出乱码
- VS的测试工具能否用CxxTest代替?
- vc++中关于视频采集的问题
- 很简单的程序编译时出错,fatal error C1001: INTERNAL COMPILER ERROR??
- 在线等待:我想让自己的程序运行到某处时等待几秒钟后继续,而且这段时间里不能太耗费cpu时间(sleep不行),怎么办?
- 高手快来
- 如何让这段程序变快一点?
- happybeyond,谢谢你的书!
- 高分,ASAPI高手请进!
- 请问各位大牛,子对话框还以再次嵌入对话框么?
- MFC删除文件失败
我后面又尝试了用WSASocket(AF_INET, SOCK_RAW, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED)创建一个原始套接字,
然后修改io控制WSAIoctl(RCVALL)选项。
发现除开ICMP差错报文,其它的数据包都是可以被recvfrom到的。
所以很困惑。
去codeproject上找了一个源码,编译之后也是相同结果。
你说会不会是系统限制?但是系统的tracert可以正常运行啊