我可以構建一個包含有用資料(DNS 請求)的 UDP 資料包的原始 IP 資料包。我可以發送它并查看它是在 Wireshark 中發送的。Wireshark 將其決議為合法的 DNS 請求,因此除了 DNS 答案外,一切看起來都很順利——我沒有得到任何答案,什么也沒有。
我的代碼(對不起,它遠非產品級代碼):
var
D:WSAData;
SendSocket, ReceiveSocket: TSocket;
bytes: Integer;
bOpt : Integer;
Buf : TPacketBuffer;
SendAddrIn : TSockAddrIn;
RecvAddIn: TSockAddrIn;
sockAddrSize: Integer;
iTotalSize : Word;
begin
try
if WSAStartup($202, D)<>0 then
begin
writeln('error..');
exit;
end;
SendSocket:=socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if SendSocket=INVALID_SOCKET then
writeln(WSAGetLastError);
// Option: Header Include
bOpt := 1;
bytes := SetSockOpt(SendSocket, IPPROTO_IP, IP_HDRINCL, @bOpt, SizeOf(bOpt));
if bytes = SOCKET_ERROR then
begin
Writeln('setsockopt(IP_HDRINCL) failed: ' IntToStr(WSAGetLastError));
exit;
end;
BuildHeaders(SrcIP, SrcPort,
DestIP, DestPort,
dns,
Buf, SendAddrIn, iTotalSize);
Writeln(inttostr(iTotalSize) ' bytes to send');
bytes := SendTo(SendSocket, buf, iTotalSize, 0, @SendAddrIn, SizeOf(SendAddrIn));
if bytes = SOCKET_ERROR then
writeln('sendto() failed: ' IntToStr(WSAGetLastError))
else
writeln('send ' IntToStr(bytes) ' bytes.');
ReceiveSocket:=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
RecvAddIn.sin_addr.s_addr := htonl(0);
RecvAddIn.sin_family := AF_INET;
RecvAddIn.sin_port := htons(SrcPort);
if bind(ReceiveSocket, TSockAddr(RecvAddIn), sizeof(RecvAddIn)) = SOCKET_ERROR then
begin
writeln('bind() failed: ' IntToStr(WSAGetLastError));
exit;
end;
FillChar(buf, SizeOf(buf), 0);
sockAddrSize := sizeof(RecvAddIn);
bytes := RecvFrom(ReceiveSocket, buf, SizeOf(buf), 0, TSockAddr(RecvAddIn), sockAddrSize);
if bytes = SOCKET_ERROR then
writeln('RecvFrom() failed: ' IntToStr(WSAGetLastError))
else
writeln('RecvFrom ' IntToStr(bytes) ' bytes.');
CloseSocket(SendSocket);
CloseSocket(ReceiveSocket);
WSACleanup;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Wireshark 將此資料包顯示為:

我嘗試創建兩個具有相同本地埠的套接字來發送和接收資料,每個套接字都有自己的型別。怎么了?..
更新:
謝謝你們的想法。實際上,接收套接字必須在任何發送之前完全初始化。但正如我發現的那樣 - 主要問題是 UDP 資料包校驗和計算。我發現一個簡單的“ping”工具生成的校驗和不等于我的代碼生成的校驗和(當然,對于相同的輸入值)。當我剛剛使用它們的值時(同樣,所有輸入值都被保留了)——DNS 服務器回傳了回應!要生成校驗和,我使用以下代碼:
function CheckSum(var Buffer; Size : integer) : Word;
type
TWordArray = array[0..1] of Word;
var
ChkSum : LongWord;
i : Integer;
begin
ChkSum := 0;
i := 0;
while Size > 1 do
begin
ChkSum := ChkSum TWordArray(Buffer)[i];
inc(i);
Size := Size - SizeOf(Word);
end;
if Size=1 then
ChkSum := ChkSum Byte(TWordArray(Buffer)[i]);
ChkSum := (ChkSum shr 16) (ChkSum and $FFFF);
ChkSum := ChkSum (Chksum shr 16);
Result := Word(ChkSum);
end;
procedure BuildHeaders(FromIP : string; iFromPort : Word;
ToIP : string; iToPort : Word;
StrMessage : TBytes; var Buf: TPacketBuffer;
var remote : TSockAddrIn; var iTotalSize: Word);
var
dwFromIP : LongWord;
dwToIP : LongWord;
iIPVersion : Word;
iIPSize : Word;
ipHdr : T_IP_Header;
udpHdr : T_UDP_Header;
iUdpSize : Word;
iUdpChecksumSize : Word;
cksum : Word;
Ptr : ^Byte;
procedure IncPtr(Value : Integer);
begin
ptr := pointer(integer(ptr) Value);
end;
begin
dwFromIP := inet_Addr(PAnsiChar(AnsiString(FromIP)));
dwToIP := inet_Addr(PAnsiChar(AnsiString(ToIP)));
iTotalSize := sizeof(ipHdr) sizeof(udpHdr) length(strMessage);
iIPVersion := 4;
iIPSize := sizeof(ipHdr) div sizeof(LongWord);
//
// IP version goes in the high order 4 bits of ip_verlen. The
// IP header length (in 32-bit words) goes in the lower 4 bits.
//
ipHdr.ip_verlen := (iIPVersion shl 4) or iIPSize;
ipHdr.ip_tos := 0; // IP type of service
ipHdr.ip_totallength := htons(iTotalSize); // Total packet len
ipHdr.ip_id := $1545; // Unique identifier: set to 0
ipHdr.ip_offset := 0; // Fragment offset field
ipHdr.ip_ttl := 128;
ipHdr.ip_protocol := $11; // Protocol(UDP)
ipHdr.ip_checksum := 0 ; // IP checksum
ipHdr.ip_srcaddr := dwFromIP; // Source address
ipHdr.ip_destaddr := dwToIP; // Destination address
iUdpSize := sizeof(udpHdr) length(strMessage);
udpHdr.src_portno := htons(iFromPort) ;
udpHdr.dst_portno := htons(iToPort) ;
udpHdr.udp_length := htons(iUdpSize) ;
udpHdr.udp_checksum := 0 ;
//
// Build the UDP pseudo-header for calculating the UDP checksum.
// The pseudo-header consists of the 32-bit source IP address,
// the 32-bit destination IP address, a zero byte, the 8-bit
// IP protocol field, the 16-bit UDP length, and the UDP
// header itself along with its data (padded with a 0 if
// the data is odd length).
//
iUdpChecksumSize := 0;
ptr := @buf[0];
FillChar(Buf, SizeOf(Buf), 0);
Move(ipHdr.ip_srcaddr, ptr^, SizeOf(ipHdr.ip_srcaddr));
IncPtr(SizeOf(ipHdr.ip_srcaddr));
iUdpChecksumSize := iUdpChecksumSize sizeof(ipHdr.ip_srcaddr);
Move(ipHdr.ip_destaddr, ptr^, SizeOf(ipHdr.ip_destaddr));
IncPtr(SizeOf(ipHdr.ip_destaddr));
iUdpChecksumSize := iUdpChecksumSize sizeof(ipHdr.ip_destaddr);
IncPtr(1);
Inc(iUdpChecksumSize);
Move(ipHdr.ip_protocol, ptr^, sizeof(ipHdr.ip_protocol));
IncPtr(sizeof(ipHdr.ip_protocol));
iUdpChecksumSize := iUdpChecksumSize sizeof(ipHdr.ip_protocol);
Move(udpHdr.udp_length, ptr^, sizeof(udpHdr.udp_length));
IncPtr(sizeof(udpHdr.udp_length));
iUdpChecksumSize := iUdpChecksumSize sizeof(udpHdr.udp_length);
move(udpHdr, ptr^, sizeof(udpHdr));
IncPtr(sizeof(udpHdr));
iUdpChecksumSize := iUdpCheckSumSize sizeof(udpHdr);
Move(StrMessage[1], ptr^, Length(strMessage));
IncPtr(Length(StrMessage));
iUdpChecksumSize := iUdpChecksumSize length(strMessage);
cksum := checksum(buf, iUdpChecksumSize);
udpHdr.udp_checksum := $FA8B;//cksum;
//
// Now assemble the IP and UDP headers along with the data
// so we can send it
//
FillChar(Buf, SizeOf(Buf), 0);
Ptr := @Buf[0];
Move(ipHdr, ptr^, SizeOf(ipHdr)); IncPtr(SizeOf(ipHdr));
Move(udpHdr, ptr^, SizeOf(udpHdr)); IncPtr(SizeOf(udpHdr));
Move(StrMessage[0], ptr^, length(StrMessage));
remote.sin_family := AF_INET;
remote.sin_port := htons(iToPort);
remote.sin_addr.s_addr := dwToIP;
end;
如果有人有另一個運行良好的實作,請分享...
uj5u.com熱心網友回復:
好的,我在校驗和計算中發現了錯誤。
下一個版本可以正常作業并生成正確的校驗和:
function CheckSum(var Buffer; Size : integer) : Word;
type
TWordArray = array[0..1] of Word;
var
ChkSum : LongWord;
i : Integer;
Item: Word;
begin
ChkSum := 0;
i := 0;
while Size > 1 do
begin
Item := TWordArray(Buffer)[i];
Item := Swap(Item);
ChkSum := ChkSum Item;
inc(i);
Size := Size - SizeOf(Word);
end;
if Size=1 then
ChkSum := ChkSum Byte(TWordArray(Buffer)[i]);
ChkSum := (ChkSum shr 16) (ChkSum and $FFFF);
ChkSum := not ChkSum;
// ChkSum := ChkSum (Chksum shr 16);
Result := Word(ChkSum);
end;
如果您發現它有任何問題,請分享您的想法。
uj5u.com熱心網友回復:
您正在創建單獨的套接字來發送和接收 DNS 資料包,但是在發送請求后您正在創建接收套接字。回應可能/很可能在接收套接字準備好之前到達(使用 Wireshark 確認),在這種情況下,回應將被作業系統簡單地丟棄。
在發送請求之前,您需要充分準備接收套接字。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/460946.html
上一篇:未能初始化SUSIAPIdll
