Program the Raw Socket Programming Under C # Talk to Socket Programming, you may think of QQ and IE, yes. There are also many network tools such as P2P, NetMeeting, etc. Applications implemented by the application layer, but also to be implemented with socket. Socket is a network programming interface that implements the network application layer, and Windows Socket includes a system component that takes advantage of the characteristics of Microsoft Windows messaging. The Socket specification version 1.1 is released in January 1993 and is widely used in the Windows9x operating system that will thereafter. Socket Specification 2.2 (which is Winsock2.2 on the Windows platform, also called Winsock2) In May 1996, Windows NT 5.0 and later version of Windows system support Winsock2, in Winsock2, support multiple transport protocols Original socket, overlapping I / O model, service quality control, etc. This article introduces some of Windows Sockets to the programming of the raw socket (Raw Socket) implemented with C #, and the network packet monitoring technology implemented on this basis. Compared with Winsock1, Winsock2 is the most obvious that supports the RAW Socket socket type. Using RAW socket, you can set the network card into a mixed mode. In this mode, we can receive the IP package on the network, of course, including the purpose Not a native IP package, through the original socket, we can also control the various protocols under Windows more freely, and can control the transmission mechanism of the network underlayer. In this example, I implemented the RawSocket class in the NBYTE.BASICCLASS namespace, which contains core technologies we implements packet monitoring. Before you implement this class, you need to write an IP header structure to temporarily store some information about the network package: [structLayout (layoutkind.explicit)] public struct ipheader {[fieldoffset (0)] public byte ip_verlen; // i4 bit First length 4-bit IP version number [Fieldoffset (1)] public byte ip_tos; // 8-bit service type TOS [fieldoffset (2)] public ushort ip_totallength; // 16-bit packet total length (bytes) [fieldoffset 4)] public ushort ip_id; // 16-bit identifier [fieldoffset (6)] public ushort ip_offset; // 3-bit flag [Fieldoffset (8)] public byte ip_ttl; // 8-bit survival time TTL [fieldoffset (9) ] Public Byte IP_Protocol; // 8-bit protocol (TCP, UDP, ICMP, ETC.) [Fieldoffset (10)] public ushort ip_checksum; // 16-bit IP header checksum [Fieldoffset (12)] public uint ip_srcaddr; / / 32-bit source IP address [fieldoffset (16)] public uint ip_destaddr; // 32 bit of IP address} This way, when each packet arrives, the data stream in the package can be converted into one iPheader object. .
Let's start writing the Rawsocket class. First, a few parameters are defined first, including: private bool error_occurred; // Sockets Whether to generate error public bool keeprunning when receiving the package; // Continue Private Static int LEN_RECEIVE_BUF; / / Get the length of the data stream byte [] receive_buf_bytes; // Received byte private socket socket = null; // The declaration socket also has a constant: const Int sio_rcvall = unchecked ((int) 0x98000001); // Monitoring All Packets The SiO_rcvall here is that the RawSocket receives all the packets, in the later IOCONTRL function, in the following constructor, initialization of some variable parameters: public rawsocket () // Constructor {error_occurred = false; len_receive_buf = 4096; receive_buf_bytes = new byte [len_receive_buf];} implement the following function creates RawSocket, and bring it to the endpoint (IPEndPoint: local IP and port) binding: public void CreateAndBindSocket (string IP ) // establish and bind socket {socket = new socket (addressfamily.internetwork, sockettype.raw, protocoltype.ip); socket.blocking = false; // Set Socket Non-Block State Socket.bind (New Ipendpain .PARSE (IP), 0)); // Binding socket IF (setsocketoption () == false error_occurred = true;} where a SOC is created Ket = new socket (addressfamily.internetwork, sockettype.raw, protocoltype.IP); 3 parameters: The first parameter is set to set the address family, the description on the MSDN is "Specify the address instance to resolve the address. Program " ). The second parameter set type is the type of RAW we use. SocketType is an enumeration data type, and the RAW socket type supports access to the underlying Transfer Protocol. By using sockettype.raw, you can communicate using the Transmission Control Protocol (TCP) and User Data Normalization (UDP), or use the Internet Control Protocol (ICMP) and Internet Group Management Protocol (IGMP) to communicate. When sending, your application must provide a complete IP header. The received datagram is returned while returning the IP header and option constant.
The third parameter setting protocol type, the Socket class uses the ProtocolType enumeration data type to notify the requested protocol to the Windows Socket API. The IP protocol is used here, so use the protocoltype.ip parameter. There is a custom setSocketoption function in the CreateAndBindSocket function, which is different from the setSocketopption in the socket class. We define the setSocketoption with the IO control function. It is defined as follows: private bool setsocketoption () // Setting Raw Socket { Bool ret_value = true; try {socket.setsocketoption (socketoptionne.ip, socketoptionname.headerincluded, 1); byte [] in = new byte [4] {1, 0, 0, 0}; byte [] out = new byte [ 4]; // Low level mode of operation accepts all packets, this step is the key, you must set up Socket to RAW and IP Level to use SiO_RcVall int RET_CODE = Socket.iocontrol (SiO_RCVALL, IN, OUT); RET_CODE = OUT [0] OUT [1] OUT [2] OUT [3]; // Synthes 1 32-bit integer IF (RET_CODE! = 0) RET_VALUE = false;} catch (socketexception) RET_VALUE = false;} Return Ret_Value;} where the setup option must be set to contain the IP cladding, otherwise the iPheter structure cannot be populated, and packet information cannot be obtained. int RET_CODE = Socket.ioControl (SiO_RCVALL, IN, OUT); is the most critical step in the function, because we can't use the Receive function in Windows to receive data on the Raw Socket, because all IP packs It is first submitted to the system core, then transfer to the user program, when sending a RAWS Socket package (such as SYN), the core does not know, there is no record that is sent or established, so when the remote host When responding, the core of the system throws all of these packages so that it is not possible. Therefore, it is not possible to simply use the received function to receive these datagrams. To reach the purpose of receiving data, you must use sniffing, receive all passed packets, and then filter, leaving the need to meet what we need. You can display the data packets on all networks by setting SiO_RCVALL. Let's introduce the IOCONTROL function. MSDN explains that it is to set the socket for low level mode, how low level operation method? 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 operand lpvoid lpvinbuffer, // pointing to the pointer DWORD cbinbuffer of the input data stream, // Enter the size of the data stream (word Number of: LPVOID LPVOUTBUFFER, / / Pointer DWORD CBOUTBUFFER, / / Output Data Stream (byte) LPDWORD LPCBBYTESRETURNED, / / Point to the real value of output byte stream LPWSAOVERLAPPED LPOVERLAPPED, // Point to one WSAOVERLAPPED Structure LPWSAOVERLAPPED_COMPLETION_ROUTINE LPCOMPETIONROUTINE / / The routines that are executed when the operation is complete); the IoControl function of the C # is not as complex as the WSAIOctL function, which includes only the control operation code, input byte stream, output byte three parameters, but These three parameters are already enough. We see a one-byte array in the function: byte [] in = new byte [4] {1, 0, 0, 0} In fact, it is a value of 1 DWORD or INT32, the same Byte [] OUT = New byte [4]; also, it is a value that is interested in the WSAiocTl function.
Because an error may occur when setting the socket option, you need to deliver the error flag with a value: public bool erroroccot {get {return error_occurred;}} The following functions implemented the received packet reception: // Analyze the received packet, formation PacketArrivedEventArgs event data class object, and initiator 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; // data stream as a whole and Ipheter structure E.HeaderLength = (UINT) (Head-> ip_verlen & 0x0f) << 2; Temp_Protocol = head-> ip_protocol; switch (temp_protocol) // extraction 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 the IP protocol version e.IPVersion = temp_version.ToString (); // The following statement extracting an additional object parameters temp_ip_srcaddr PacketArrivedEventArgs = 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; // assigns the IP head in BUF to iPheaderBuffer Array.copy in PacketarriveDeventargs; 0, (int) E.HEADERBUFER, 0, (int) E.HEADERLENGTH); // Assignment in the package in BUF Give the MessageBuffer Array.copy in PacketarriveDeventargs (Buf, (int)); E.MESSAGELENGTH, (INT) E.MESSAGELENGTH);} // triggers Packetarrival Event OnPacketarrival (e);} Everyone is noted, on it In the function, we use the so-called unsafe code of the pointer, which can be seen in the C #, the pointer and shift operations can also bring programmers to make programmers. Declaring the PacketarriveDeventargs class object in a function to pass the information package information through the event through the event. The PacketarriveDeventargs class is the nested class in the RawSocket class, which inherits the system event (Event) class, encapsulated information contained in other data clauses of packets, ports, protocols. In the function of starting the received packet, we use the asynchronous operation method. The following functions open the interface of asynchronous listening: public void run () // Start listening {ioasyncResult ar = socket.beginReceive (Receive_BUF_BYTES, 0, LEN_RECEIVE_BUF, SocketFlags .None, new asyncCallback (callReceive), this);} Socket.BeginReceive function returns an asynchronous operation interface, and declares that the asynchronous callback function CallReceive is declared in the generated function of this interface, and streams the received network data to receive_buf_bytes, so that the interface can be used with a parameter of asynchronous operation asynchronous callback continuously receive packets: 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 receives a new packet when it hangs or ends asynchronous read, so that each packet can be detected by programs.