int query_ntp_server()

in OSNetworkRequirementChecker-PC/main.cpp [131:270]


int query_ntp_server(const char *hostname, int ntp_port, int src_port)
{
	int iRes = 0;

	// Build the NTP packet
	ntp_packet packet = { 0 };
	packet.li = 0;		// No warning
	packet.vn = 4;		// Version 4 (aka IPv4-only)
	packet.mode = 3;	// Client Mode


	// Log the requested hostname.
	std::cout << "- time from " << hostname;

#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER)
	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		iRes = getSocketErrorCode();
		std::cerr << "WSAStartup() failed with error code " << iRes << std::endl;

		WSACleanup();
		return iRes;
	}
#endif

	// Does the host exist?
	struct hostent *server = gethostbyname(hostname);
	if (server == NULL)
	{
		iRes = getSocketErrorCode();
		std::cerr << "<???> --> gethostbyname() error " << iRes << std::endl;
	}
	else
	{
		// Setup the server address
		struct sockaddr_in server_addr;
		memset(&server_addr, 0, sizeof(server_addr));
		server_addr.sin_family = AF_INET;
		server_addr.sin_addr.s_addr = *(long *)(server->h_addr_list[0]);
		server_addr.sin_port = htons(ntp_port);
		char server_ip_addr[INET_ADDRSTRLEN];
		sprintf(server_ip_addr, "%s", inet_ntoa(*((struct in_addr *)server->h_addr)));

		// Log the resolved IP address.
		std::cout << "<" << server_ip_addr << "> --> ";

		// Setup the source address (for source port customization)
		struct sockaddr_in client_addr;
		memset(&client_addr, 0, sizeof(client_addr));
		client_addr.sin_family = AF_INET;
		client_addr.sin_addr.s_addr = htonl(INADDR_ANY);

		// Optionally, set the given outbound port
		if (src_port > 0)
		{
			client_addr.sin_port = htons(src_port);
		}

		// Open an UDP socket
		int sock_fd = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
		if (sock_fd < 0)
		{
			iRes = getSocketErrorCode();
			std::cerr << "socket() open error " << iRes << std::endl;
		}
		else
		{
			// Set a receive timeout
#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER)
			DWORD timeout = SOCKET_TIMEOUT_SEC * 1000;
			if (setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(timeout)) < 0)
			{
				iRes = getSocketErrorCode();
				std::cerr << "setsockopt() failed with error " << iRes << std::endl;
			}
#else
			struct timeval tv;
			tv.tv_sec = SOCKET_TIMEOUT_SEC;
			tv.tv_usec = 0;
			if (setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv)) < 0)
			{
				iRes = getSocketErrorCode();
				std::cerr << "setsockopt() failed with error " << iRes << std::endl;
			}
#endif
			// Bind it to the client w/optional desired source port
			if (bind(sock_fd, (struct sockaddr *)&client_addr, sizeof(client_addr)) < 0)
			{
				iRes = getSocketErrorCode();
				std::cerr << "bind() failed with error " << iRes << std::endl;
			}
			else
			{
				// Send the NTP packet
				int sent_bytes = sendto(sock_fd, (const char *)&packet, sizeof(packet), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
				if (sent_bytes < 0)
				{
					iRes = getSocketErrorCode();
					std::cerr << "sendto() failed with error " << iRes << std::endl;
				}
				else
				{
					// Receive the NTP data back
					socklen_t client_addr_len = sizeof(client_addr);
					int recv_bytes = recvfrom(sock_fd, (char *)&packet, sizeof(packet), 0, (struct sockaddr *)&client_addr, &client_addr_len);
					if (recv_bytes < 0)
					{
						iRes = getSocketErrorCode();
						std::cerr << "recvfrom() failed with error " << iRes << std::endl;
					}
					else
					{
						// Firstly, the "endianness" of txTm_s needs to be converted from the network's big-endian to the local host's one.
						// txTm_s contains the number of seconds passed since 00:00:00 UTC Jan 1st 1900, as of when the packet left the NTP server.
						packet.txTm_s = ntohl(packet.txTm_s);

						// The Unix epoch is the number of seconds since 00:00:00 UTC Jan 1st 1970,
						// therefore we subtract 70 years worth of seconds from the time returned by the NTP server.
						time_t txTm = (time_t)((uint64_t)packet.txTm_s - NTP_TIMESTAMP_DELTA);

						// Print the time we got from the NTP server, accounting the local timezone and conversion from UTC time.
						std::cout << ctime((const time_t *)&txTm);
					}
				}
			}
#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER)
			closesocket(sock_fd);
#else
			close(sock_fd);
#endif
		}
	}

#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER)
	WSACleanup();
#endif

	return iRes;
}