OSNetworkRequirementChecker-PC/main.cpp (300 lines of code) (raw):
#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER)
# define _WINSOCK_DEPRECATED_NO_WARNINGS
# ifndef _CRT_SECURE_NO_WARNINGS
# define _CRT_SECURE_NO_WARNINGS
# endif
# pragma comment(lib, "Ws2_32.lib")
# include <WS2tcpip.h>
# include <winsock2.h>
#else
# include <sys/types.h>
# include <sys/socket.h>
# include <unistd.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <netdb.h>
#endif
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <iostream>
const char *const ntpServers[] =
{
"time.windows.com",
"time.sphere.azure.net",
"prod.time.sphere.azure.net",
"168.61.215.74",
"129.6.15.28",
"20.43.94.199", "20.189.79.72",
"40.81.94.65", "40.81.188.85", "40.119.6.228", "40.119.148.38",
"20.101.57.9",
"51.137.137.111", "51.145.123.29",
"52.148.114.188", "52.231.114.183",
// Termination element
""
};
typedef struct
{
const char *hostname;
int port;
} t_endpoint;
const t_endpoint endpoints[] =
{
{ "Device provisioning and communication with IoT Hub:", -1 }, // Group description
{ "global.azure-devices-provisioning.net", 8883 },
{ "global.azure-devices-provisioning.net", 443 },
{ "Internet connection checks, certificate file downloads, and similar tasks:", -1 }, // Group description
{ "www.msftconnecttest.com", 80 },
{ "prod.update.sphere.azure.net", 80 },
{ "Communication with web services and Azure Sphere Security service:", -1 }, // Group description
{ "anse.azurewatson.microsoft.com", 443 },
{ "prod.core.sphere.azure.net", 443 },
{ "prod.device.core.sphere.azure.net", 443 },
{ "prod.deviceauth.sphere.azure.net", 443 },
{ "prod.dinsights.core.sphere.azure.net", 443 },
{ "prod.releases.sphere.azure.net", 443 },
{ "prodmsimg.blob.core.windows.net", 443 },
{ "prodmsimg-secondary.blob.core.windows.net", 443 },
{ "prodptimg.blob.core.windows.net", 443 },
{ "prodptimg-secondary.blob.core.windows.net", 443 },
{ "sphereblobeus.azurewatson.microsoft.com", 443 },
{ "sphereblobweus.azurewatson.microsoft.com", 443 },
{ "sphere.sb.dl.delivery.mp.microsoft.com", 443 },
// Termination element
{ "", 0}
};
#define NTP_PORT 123 // NTP standard listening port
#define NTP_PORT_OUT 124 // NTP requests from Azure Sphere are sourced through local port 124
#define NTP_TIMESTAMP_DELTA 2208988800ull // 70 years in seconds
#define SOCKET_TIMEOUT_SEC 10 // The socket timeout for receiving (in seconds)
typedef struct
{
union
{
struct
{
uint8_t mode : 3; // mode - i.e. client will pick mode 3 for client
uint8_t vn : 3; // vn - Version number of the protocol.
uint8_t li : 2; // li - Leap indicator
};
uint8_t li_vn_mode; // 8 bits. li, vn, and mode.
};
uint8_t stratum; // 8 bits. Stratum level of the local clock.
uint8_t poll; // 8 bits. Maximum interval between successive messages.
uint8_t precision; // 8 bits. Precision of the local clock.
uint32_t rootDelay; // 32 bits. Total round trip delay time.
uint32_t rootDispersion; // 32 bits. Max error aloud from primary clock source.
uint32_t refId; // 32 bits. Reference clock identifier.
uint32_t refTm_s; // 32 bits. Reference time-stamp seconds.
uint32_t refTm_f; // 32 bits. Reference time-stamp fraction of a second.
uint32_t origTm_s; // 32 bits. Originate time-stamp seconds.
uint32_t origTm_f; // 32 bits. Originate time-stamp fraction of a second.
uint32_t rxTm_s; // 32 bits. Received time-stamp seconds.
uint32_t rxTm_f; // 32 bits. Received time-stamp fraction of a second.
uint32_t txTm_s; // 32 bits. Transmit time-stamp seconds.
uint32_t txTm_f; // 32 bits. Transmit time-stamp fraction of a second.
} ntp_packet; // Total: 384 bits or 48 bytes.
int query_ntp_server(const char *hostname, int ntp_port, int src_port);
int resolve_hostname(const char *hostname, int port);
int getSocketErrorCode(void)
{
#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER)
// https://learn.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2
return WSAGetLastError();
#else
return errno;
#endif
}
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;
}
int resolve_hostname(const char *hostname, int port)
{
int iRes = 0;
#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?
std::cout << "- Resolving " << hostname << "... ";
struct hostent *server = gethostbyname(hostname);
if (server == NULL)
{
iRes = getSocketErrorCode();
std::cout << "FAILED (errno=" << iRes << ")" << std::endl;
}
else
{
// Retrieve 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]);
// Let's attempt a dummy connection
std::cout << "success --> connecting to " << inet_ntoa(*((struct in_addr *)server->h_addr)) << ":" << port << "... ";
int sock_fd = (int)socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd < 0)
{
iRes = getSocketErrorCode();
std::cerr << "socket() open error " << iRes << std::endl;
}
else
{
server_addr.sin_port = htons(port);
if (connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
iRes = getSocketErrorCode();
std::cout << "FAILED (errno=" << iRes << ")" << std::endl;
}
else
{
#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER)
closesocket(sock_fd);
#else
close(sock_fd);
#endif
std::cout << "success!" << std::endl;
}
}
}
#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER)
WSACleanup();
#endif
return iRes;
}
int main(int argc, char **argv)
{
std::cout << "Azure Sphere network-checkup utility." << std::endl << std::endl;
try
{
std::cout << "Querying required NTP servers..." << std::endl;
for (int i = 0; *ntpServers[i]; i++)
{
query_ntp_server(ntpServers[i], NTP_PORT, NTP_PORT_OUT);
}
std::cout << std::endl << "Querying required endpoints..." << std::endl;
for (int i = 0; *endpoints[i].hostname; i++)
{
if (-1 == endpoints[i].port)
{
// This entry is a group description
std::cout << std::endl << endpoints[i].hostname << std::endl;
}
else
{
resolve_hostname(endpoints[i].hostname, endpoints[i].port);
}
}
std::cout << std::endl;
}
catch (...)
{
std::cout << std::endl << "Unexpected exception executing checks!" << std::endl;
}
return 0;
}