Compared with WINSOCK1, Winsock2 most obvious is to support the raw socket socket type, using raw socket, can set the network card into promiscuous mode, in this mode, we can receive IP packets on the network, of course, including the destination is not the local IP packet, Through the original socket, we can also more freely control the various protocols under Windows, and can control the network underlying transmission mechanism. In the example of this article, Nbyte. The Basicclass namespace implements the Rawsocket class, which contains the core technologies that enable us to implement packet monitoring. Before implementing this class, you need to write an IP header structure to temporarily hold some information about the network packets:
[StructLayout (LAYOUTKIND.EXPLICIT)] public struct Ipheader { [FieldOffset (0)] public byte Ip_verlen; I4 bit Header length + 4-bit IP version number [FieldOffset (1)] public byte Ip_tos; 8-bit Service type TOS [FieldOffset (2)] public ushort Ip_totallength; Total length of 16-bit packets (bytes) [FieldOffset (4)] public ushort ip_id; 16-bit identification [FieldOffset (6)] public ushort Ip_offset; 3-bit flag bit [FieldOffset (8)] public byte Ip_ttl; 8-bit time-to-live TTL [FieldOffset (9)] public byte Ip_protocol; 8-bit protocol (TCP, UDP, ICMP, ETC) [FieldOffset] public ushort ip_checksum; 16-bit IP header checksum [FieldOffset] public uint ip_srcaddr; 32-bit Source IP address [FieldOffset (+)] public uint ip_destaddr; 32-bit Destination IP address } |
In this way, when each packet arrives, the data in the package can be transformed into a Ipheader object with a forced type conversion.
Here we begin to write the Rawsocket class, beginning with a few parameters, including:
private bool error_occurred; Whether the socket generates an error when it receives the package public bool keeprunning; Whether to proceed private static int len_receive_buf; The length of the resulting data stream byte [] receive_buf_bytes; Bytes Received Private socket socket = NULL; Declaring sockets |
There is also a constant:
const int Sio_rcvall = unchecked ((int) 0x98000001);//listen to all packets |
The sio_rcvall here is to instruct Rawsocket to receive all the packets, to be used in subsequent Iocontrl functions, and to initialize some variable parameters in the following constructor:
Public Rawsocket ()//constructor { Error_occurred=false; Len_receive_buf = 4096; Receive_buf_bytes = new BYTE[LEN_RECEIVE_BUF]; } |
The following function implements the creation of the rawsocket and binds it to the endpoint (ipendpoint: native IP and Port):
public void Createandbindsocket (string IP)//Establish and bind sockets { Socket = new Socket (addressfamily.internetwork, Sockettype.raw, Protocoltype.ip); Socket. Blocking = false; Socket non-blocking status Socket. Bind (New IPEndPoint (Ipaddress.parse (IP), 0)); Binding sockets
if (SetSocketOption () ==false) error_occurred=true; } |
Where a sentence in the creation of a socket
Socket = new Socket (addressfamily.internetwork, Sockettype.raw, Protocoltype.ip); |
There are 3 parameters in
:
The first parameter is the set address family, and the description on MSDN is "Specify the addressing scheme that the socket instance uses to resolve the address" when binding a socket to an endpoint (IPEndPoint). You need to use the internetwork member, which is the address format for IP version 4, which is an addressing scheme (addressfamily) for most sockets programming today.
The type of socket set for the second parameter is the raw type that we use, and SocketType is an enumerated data type, and the raw socket type supports access to the underlying transport protocol. By using Sockettype.raw, you can communicate not only by using Transmission Control Protocol (TCP) and User Datagram Protocol (UDP), but also by using the inter-Internet messaging Controls (ICMP) and the Network Group Management Protocol (IGMP). Your application must provide the full IP header when it is sent. The received datagram keeps its IP header and options intact when it returns.
The third parameter sets the protocol type, and the socket class notifies the Windows socket API of the requested protocol using the ProtocolType enumeration data type. The IP protocol is used here, so use the Protocoltype.ip parameter. &NBSP
has a custom setsocketoption function in the Createandbindsocket function that differs from the setsocketoption in the socket class, What we define here is the setsocketoption with IO control, which is defined as follows:
private bool SetSocketOption ()//SET RAW socket { bool Ret_value = true; Try { Socket. SetSocketOption (Socketoptionlevel.ip, socketoptionname.headerincluded, 1); byte []in = new Byte[4]{1, 0, 0, 0}; byte []out = new byte[4];
Low-level operation mode, accept all packets, this step is the key, you must set the socket to raw and IP level to be available Sio_rcvall int ret_code = socket. IOControl (Sio_rcvall, in, out); Ret_code = out[0] + out[1] + out[2] + out[3];//synthesize 4 8-bit bytes into a 32-bit integer if (Ret_code! = 0) Ret_value = false; } catch (SocketException) { Ret_value = false; } return ret_value; } |
Where the socket option is set, the socket must contain the IP header, or the Ipheader structure cannot be populated or the packet information can be obtained.
int ret_code = socket. IOControl (Sio_rcvall, in, out); |
Is the most critical step in the function, because, in Windows we can not use the receive function to accept data on the raw socket, this is because all IP packets are handed to the system core before transmission to the user program, when sending a raws When the socket packet (such as SYN), the core does not know, there is no data is sent or connected to establish the record, so when the remote host response, the system core will be all of these packages are discarded, so that the application can not. Therefore, you cannot simply use the receive function to receive these datagrams. To achieve the purpose of receiving data, it is necessary to use sniffer, receive all the packets passed, and then filter, left to meet our needs. You can receive packets on all networks by setting Sio_rcvall. Next, let's introduce the IOControl function. MSDN explains what it says is to set the socket for low-level operation mode, how low-level operation? In fact, this function is similar to the WSAIOCTL function in the API. The WSAIOCTL function is defined as follows:
int WSAIoctl ( Socket s,//a specified socket DWORD Dwiocontrolcode,//Control operation code LPVOID Lpvinbuffer,//pointer to input data stream DWORD Cbinbuffer,//the size of the input data stream (bytes) LPVOID Lpvoutbuffer,//pointer to output data stream DWORD cbOutBuffer,//size of output data stream (bytes) Lpdword lpcbbytesreturned,//real value pointing to the number of output byte streams lpwsaoverlapped lpoverlapped,//point to a wsaoverlapped structure Lpwsaoverlapped_completion_routine lpcompletionroutine//A routine that is executed when the operation is completed ); |
C # 's IOControl function is not as complex as the WSAIOCTL function, which includes only the control operation code, input byte stream, output byte stream three parameters, but these three parameters are sufficient. We see a byte array defined in the function: Byte []in = new Byte[4]{1, 0, 0, 0} In fact it is a DWORD with a value of 1 or Int32, and the same byte []out = new byte[4]; Also, it integers an int, as the WSA The value that the parameter lpcbbytesreturned points to in the IOCTL function.
Because an error may occur when you set the socket option, you need to pass an error flag with a value:
public bool ErrorOccurred { Get { return error_occurred; } } |
The following function implements the receipt of the packet:
Parses the received packet, forms the Packetarrivedeventargs event data class object, and raises the Packetarrival event unsafe private void Receive (Byte [] buf, int len) { BYTE temp_protocol=0; UINT Temp_version=0; UINT temp_ip_srcaddr=0; UINT temp_ip_destaddr=0; Short temp_srcport=0; Short temp_dstport=0; IPAddress temp_ip;
Packetarrivedeventargs e=new Packetarrivedeventargs ();//new network packet Information event
Fixed (byte *fixed_buf = buf) { Ipheader * head = (Ipheader *) fixed_buf;//the data flow to the Ipheader structure E.headerlength= (UINT) (Head->ip_verlen & 0x0F) << 2;
Temp_protocol = head->ip_protocol; Switch (TEMP_PROTOCOL)//Extract protocol type { Case 1:e.protocol= "ICMP"; Break Case 2:e.protocol= "IGMP"; Break Case 6:e.protocol= "TCP"; Break Case 17:e.protocol= "UDP"; Break Default:e.protocol= "UNKNOWN"; Break }
Temp_version = (UINT) (Head->ip_verlen & 0xF0) >> 4;//extract IP protocol version E.ipversion = temp_version. ToString ();
The following statement extracts additional parameters from the Packetarrivedeventargs object TEMP_IP_SRCADDR = head->ip_srcaddr; TEMP_IP_DESTADDR = head->ip_destaddr; TEMP_IP = new IPAddress (TEMP_IP_SRCADDR); E.originationaddress =temp_ip. ToString (); TEMP_IP = new IPAddress (TEMP_IP_DESTADDR); E.destinationaddress = Temp_ip. ToString ();
Temp_srcport = * (short *) &fixed_buf[e.HeaderLength]; Temp_dstport = * (short *) &fixed_buf[e.HeaderLength+2]; E.originationport=ipaddress.networktohostorder (Temp_srcport). ToString (); E.destinationport=ipaddress.networktohostorder (Temp_dstport). ToString ();
E.packetlength = (UINT) len; E.messagelength = (UINT) len-e.headerlength;
E.receivebuffer=buf; Assign the IP header in the BUF to the Ipheaderbuffer in the Packetarrivedeventargs Array.copy (buf,0,e.ipheaderbuffer,0, (int) e.headerlength); Assign the contents of the package in the BUF to the Messagebuffer in the Packetarrivedeventargs Array.copy (buf, (int) e.headerlength,e.messagebuffer,0, (int) e.messagelength); } Raising the Packetarrival Event Onpacketarrival (e); } |
As you can see, in the above function, we use pointers, the so-called unsafe code, which is visible in C # and the primitive operations of pointers and shift operations also bring programming convenience to programmers. Declare the Packetarrivedeventargs class object in the function so that the packet information is passed through the event via the Onpacketarrival (e) function. Where the Packetarrivedeventargs class is a nested class in the Rawsocket class, it inherits the system event class, encapsulating the information contained in the packet's IP, port, protocol, and other packet headers. In the function that initiates the receive packet, we use the method of the asynchronous operation, the following function opens the interface of the asynchronous listener:
public void Run ()//start listening { IAsyncResult AR = socket. BeginReceive (receive_buf_bytes, 0, Len_receive_buf, Socketflags.none, New AsyncCallback (Callreceive), this); } |
The Socket.beginreceive function returns an interface to an asynchronous operation and declares the asynchronous callback function callreceive in the generated function BeginReceive of the interface, and spreads the received network data to the receive_buf_bytes. This allows an asynchronous callback function with an interface parameter with an asynchronous operation to continuously receive the packet:
private void Callreceive (IAsyncResult ar)//asynchronous callback { int received_bytes; Received_bytes = socket. EndReceive (AR); Receive (Receive_buf_bytes, received_bytes); if (keeprunning) Run (); } |
This function will receive a new packet after suspending or ending the asynchronous read, which guarantees that each packet can be detected by the program.
The following communicates with the outside world by declaring the proxy event handle:
public delegate void Packetarrivedeventhandler (Object sender, Packetarrivedeventargs args); Event handle: Raises an event when a package arrives Public event Packetarrivedeventhandler packetarrival;//declaration time handle function |
In this way, the data packet information can be obtained, the asynchronous callback function can be used to improve the efficiency of the receiving packet, and the packet information is passed to the outside world through the agent event. Since all the packet information can be passed out, you can achieve the analysis of the packet:) but Rawsocket's task is not finished, and finally do not look at the closure of the socket AH:
public void Shutdown ()//close RAW socket { if (socket! = NULL) { Socket. Shutdown (Socketshutdown.both); Socket. Close (); } } |
The above describes the Rawsocket class through the construction of IP headers to obtain the information in the packet, and through the asynchronous callback function to achieve the receipt of the packet, and the use of the time agent handle and the custom packet information event class to send the packet information, so as to achieve the network packet monitoring, So that we can add some functions outside to analyze the packet.
C # 's RAW socket for network packet monitoring