Introduction to winsock programming

1, Statement

In the process of self-study, I record my learning experience here. If there is anything wrong with the content, please criticize or write a private letter below. Thank you for reading.

2, Common basic functions

2.1 header file

When using WinSocket to write programs, you need to use several important files, winsock2.h and static link library file ws2_32.lib, and dynamic link library ws2_32.dll, the header file is used in the source program, the static link library is used to compile programs based on Winsock, and the dynamic link library ws2_32.dll is necessary to run WinSock based programs.

2.2 function

Bibliography: network security programming technology and examples, 2008.
Reference documents: Microsoft documentation

2.2.1 htonl function

Set the host's u_ The long value is converted to network byte order (32 bits).

//prototype 	 u_long htonl(u_long hostlong)
#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
void main(){
	u_long a = 0x12345678;
	u_long b = htonl(a);
	printf("%x\n", b);
}

2.2.2 ntohl function

Converts the 32-bit network byte order to host bytes.

//prototype 	 u_long ntohl(u_long netlong)
#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
void main(){
	u_long a = 0x12345678;
	u_long b = ntohl(a);
	printf("%x\n", b);
}

2.2.3 ntohs function

Converts 16 bit network bytes to host bytes.

//prototype 	 u_short ntohs(u_short netshort)
#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
void main(){
	u_short a = 0x12345678;
	u_short b = ntohs(a);
	printf("%x\n", b);
}

2.2.4 htons function

Converts 16 bit host bytes to network byte order.

//prototype 	 u_short htons(u_short hostshort)
#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
void main(){
	u_short a = 0x12345678;
	u_short b = htons(a);
	printf("%x\n", b);
}

2.2.5 inet_pton and inet_ntop function

reference resources This article (click to read) the article explains these two functions, and we will give an example directly here.

#include<WinSock2.h>
#include<stdio.h>
#pragma comment(lib, "ws2_32.lib")
#include <Ws2tcpip.h>
void main(){
	char a[] = "192.168.1.3";	//Create an IP address as a string
	struct in_addr s;			//Create a variable that accepts IP address translation to store the binary form
	if (inet_pton(AF_INET, a, &s) == 1)printf("ok\n");	//If successful, 1 will be returned
	printf("%s->%u\n", a, s);
	if (inet_ntop(AF_INET, &s, a, sizeof(a)) != NULL)puts("ok");	//Switch back
	printf("%s->%u\n", a, s);	
}

Where structure in_addr is:

struct in_addr{
	union{
		struct{ u_char s_b1, s_b2, s_b3, s_b4;}S_un_b;
		struct{ u_short s_w1, s_w2;} S_un_w;
		u_long S_addr;
	}S_un;
};

At present, we only need to pay attention to u_long S_addr is OK.

2.2.6 WSAStartup and WSACleanup functions

WSAStartup is used to initialize Winsock. It should be called before using socket function. Its essence is to call the appropriate Winsock dynamic link library.

int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);

The first parameter is the WinSock version, which is represented by the MAKEWORD macro. For example, MAKEWORD (2,1) represents version 2.1.
The second parameter represents the WSAData pointer, which is used to store socket information.

typedef struct WSAData{...}WSADATA,*LPWSADATA;

If its content needs Baidu keyword.

int WSACleanup(void);

This function stops using Winsock 2 DLL.

2.2.7 gethostname function

The secondary function is used to obtain the host name. Look at the example directly.

#include<WinSock2.h>
#include<stdio.h>
#pragma comment(lib, "ws2_32.lib")
#include <Ws2tcpip.h>
void main()[
	char hostname[100];		//Array of host strings
	WSADATA wsaData;
	int Ret;
	if (Ret = WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
		printf("uperror %d\n", Ret);
		exit(EXIT_SUCCESS);
	}
	//Use of functions
	if (gethostname(hostname, sizeof(hostname)) == SOCKET_ERROR) {
		printf("erroe gethostname");
		exit(EXIT_SUCCESS);
	}else {
		printf("hostname:%s\n", hostname);
	}
	if (WSACleanup() == SOCKET_ERROR) {
		printf("cleanerror\n");
		exit(0);
	}
}

2.2.8 getaddrinfo and getnameinfo functions

Two Chinese explanations are recommended getaddrinfo and addrinfo . (actually the same)
In addition, we can also have a look Original official documents.
Here is my opinion:
(first of all, I'll extract some from the above content for easy viewing. If there is any dispute, I can send a private letter)

getaddrinfo:

getaddrinfo definition:

int getaddrinfo(const char *restrict nodename, /* host Or IP address */
    const char *restrict servname, /* Decimal port number or common service name, such as "ftp", "http", etc */
    const struct addrinfo *restrict hints, /* Get information request settings */
    struct addrinfo **restrict res); /* Get information results */

void freeaddrinfo(struct addrinfo *ai); 

The first two parameters are not enough. Let's look at the last two parameters. They are the same structure pointers. hints is used to set and receive the information of this structure. res is the pointer to the structure pointer used to receive the final information, so we need to pass in a pointer address.
**Return value: * * 0 is returned successfully. Otherwise, a non-zero value is returned. Use gai_strerrorA (int ecode) accepts a returned string pointer to check for errors.
freeaddrinfo is used to release the memory space of the obtained addrinfo structure or structure linked list.
Related structures:
addrinfo definition:
come from

struct addrinfo {
    int ai_flags;   /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
    int ai_family;  /* PF_xxx */
    int ai_socktype;    /* SOCK_xxx */
    int ai_protocol;    /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
    socklen_t ai_addrlen;   /* length of ai_addr */
    char    *ai_canonname;  /* canonical name for hostname */
    struct  sockaddr *ai_addr;  /* binary address */
    struct  addrinfo *ai_next;  /* next structure in linked list */
};

When initializing this structure, we usually initialize it to 0. (for now)
Here, we will focus on several structures and analyze them together:
sockaddr definition:

struct sockaddr
{
    sa_family_t  sa_family;   //ushort type 
    char         sa_data[14]; // Socket address (variable-length data). 
};

sockaddr_in definition:

struct sockaddr_in
{
    sa_family_t     sin_family;   //AF_INET. 
    in_port_t       sin_port;     //Port number 
    struct in_addr  sin_addr;     //IP address (network byte order)
    char[8] sin_zero;			//I don't know the purpose yet
};

sockaddr_in6 definition:

struct sockaddr_in6{
	u_short sin6_family;
	u_short sin6_port;
	u_long sin6_flowinfo;
	in6_addr sin6_addr;
	u_long sin6_scope_id;
}
typedef struct sockaddr_in6 SOCKADDR_IN6;
typedef struct sockaddr_in6 *PSOCKADDR_IN6;
typedef struct sockaddr_in6 FAR *LPSOCKADDR_IN6;

sockaddr_in and sockaddr_in6 corresponds to IPv4 and IPv6 respectively.
I have simply drawn a connection between the above structures for reference only:

Secondly, sockaddr can be forcibly converted to IPv4 and IPv6 structure types during use. We will find that the memory size of IPv6 structure type is inconsistent with that of sockaddr, but the information can be well preserved during the forcible conversion. With the help of my friends, we guess that it should be in the use of built-in functions, There is a memory allocation problem (because there are pointers in the structure), so the forced conversion can be implemented to deal with different IP addresses.

getnameinfo:

getnameinfo definition:

INT WSAAPI getnameinfo(
  const SOCKADDR *pSockaddr,	//sockaddr structure containing address and port number
  socklen_t      SockaddrLength,	//The byte length of an address structure passed in
  PCHAR          pNodeBuffer,	//A string used to receive the returned hostname
  DWORD          NodeBufferSize,	//It is used to allocate space for receiving host name strings. Generally, sizeof is used
  PCHAR          pServiceBuffer,	//Similarly, it is the name of the receiving service
  DWORD          ServiceBufferSize,	//Similarly, sizeof is generally used
  INT            Flags
);

Reference documents: link
The getnameinfo function can resolve address to host name, port number to service name.
Parameter Description:

  • pSockaddr if it is not sockaddr type, the corresponding IPv4 passes sockaddr_in, IPv6 pass sockaddr_in6.
  • If SockaddrLength is an addrinfo pointer, we can use AI in this structure_ addrlen. For IPv4 or IPv6, we use the sizeof operator to calculate the size of the address structure.
  • pNodeBuffer and pServiceBuffer need to allocate memory space in advance.
  • Flags have the following settings:
    NI_ When the namereqd is set, if the host name cannot be resolved by the domain name system (DNS), error is returned.
    NI_ When the nofqdn is set, it will return to the host parameter a relative distinguished name of the local host. (I don't know. I'd better read the English of the original document o(╥﹏╥) o)
    NI_ When numerichost is set, it will return a numeric form of host name instead of its own name. If its name cannot be resolved by the domain name system (DNS), it will also be returned.
    NI_ When numericserv is set, the service port number is returned. At the same time, if the host name does not set an IP address, the host name will be returned as an IP address.
    NI_DGRAMSetting the NI_DGRAM flag indicates that the service is a datagram service. This flag is necessary for the few services that provide different port numbers for UDP and TCP service.

Return value: zero is returned successfully. If it is non-zero, use wsagetlastererror (void) to check the error (integer return value). The error list can be searched by yourself.

family:

AF_ IPv4 corresponding to INET;
AF_INET6 corresponds to IPv6;
AF_UNSPECIPv4 and IPv6 are OK.
For the remaining parameters, please refer to file

example:

Combined with the host name obtained earlier, let's resolve the IP.

	//Address resolution
	char hostname[100];
	WSADATA wsaData;
	int Ret;
	if (Ret = WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
		printf("uperror");
		exit(EXIT_SUCCESS);
	}
	if (gethostname(hostname, sizeof(hostname)) == SOCKET_ERROR) {
		printf("erroe gethostname");
		exit(EXIT_SUCCESS);
	}else {
		printf("hostname:%s\n", hostname);
	}
	
	//Set the received information, create the structure pointer getaddr to receive the returned information, and allocate space in the function
	addrinfo *addrstr, *getaddr;
	addrstr = (addrinfo*)malloc(sizeof(addrinfo));
	//Initialize to 0
	memset(addrstr, 0, sizeof(addrinfo));
	//The address family type is set to AF_UNSPEC, either IPv4 or IPv6
	addrstr->ai_family = AF_UNSPEC;

	//Create temporary variable
	sockaddr_in* ipv4;
	sockaddr_in6* ipv6;

	int s;
	if (s = getaddrinfo(hostname, "https", addrstr, &getaddr) != 0) {
		printf("error getaddrstr:%s", gai_strerror(s));
		exit(EXIT_SUCCESS);
	}
	//Create memory space to receive the converted string
	char buf[1025];

	for (addrinfo* re = getaddr; re != NULL; re = re->ai_next) {
		switch(re->ai_family) {
		case AF_INET:
			ipv4 = (sockaddr_in*)re->ai_addr;
			inet_ntop(re->ai_family, &ipv4->sin_addr, buf, sizeof(buf));
			break;
		case AF_INET6:
			ipv6 = (sockaddr_in6*)re->ai_addr;
			inet_ntop(re->ai_family, &ipv6->sin6_addr, buf, sizeof(buf));
			break;
		}
		printf("[ipv%d]:%s\n", re->ai_family == AF_INET ? 4: 6, buf);

	}

	puts("--------------------------------");

	//In turn, the getnameinfo function is applied to parse
	int ret = 0;
	addrinfo* res_p;
	for (res_p = getaddr; res_p != NULL; res_p = res_p->ai_next) {
		char host[1024], servername[1024] = {0};
		memset(host, 0, sizeof(host));
		puts("going");
		ret = getnameinfo(res_p->ai_addr, res_p->ai_addrlen, host, sizeof(host), servername, sizeof(servername), NI_NAMEREQD);
		if (ret != 0)
		{
			printf("getnameinfo: %s\n", gai_strerror(ret));
		}
		else
		{
			printf("hostname: %s	%s\n", host, servername);
		}
	}
	
	//Release the getaddr pointer space just created
	freeaddrinfo(getaddr);
	if (WSACleanup() == SOCKET_ERROR) {
		printf("cleanerror\n");
		exit(0);
	}

2.2.9 WSAGetLastError function

Errors encountered in WinSock programming can be checked with this function.

int WSAGetLastError(void);

Tags: C++ Cyber Security

Posted on Sat, 09 Oct 2021 00:10:44 -0400 by Newladder