1 /*
2  * Copyright (c) 2017 Linaro Limited
3  * Copyright (c) 2019 Intel Corporation
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <zephyr/logging/log.h>
9 LOG_MODULE_REGISTER(net_sntp_client_sample, LOG_LEVEL_DBG);
10 
11 #include <zephyr/net/socket.h>
12 #include <zephyr/net/socket_service.h>
13 #include <zephyr/net/sntp.h>
14 #include <arpa/inet.h>
15 #include <netdb.h>
16 
17 #include "net_sample_common.h"
18 
19 static K_SEM_DEFINE(sntp_async_received, 0, 1);
20 static void sntp_service_handler(struct net_socket_service_event *pev);
21 
22 NET_SOCKET_SERVICE_SYNC_DEFINE_STATIC(service_sntp_async, sntp_service_handler, 1);
23 
dns_query(const char * host,uint16_t port,int family,int socktype,struct sockaddr * addr,socklen_t * addrlen)24 int dns_query(const char *host, uint16_t port, int family, int socktype, struct sockaddr *addr,
25 			  socklen_t *addrlen)
26 {
27 	struct addrinfo hints = {
28 		.ai_family = family,
29 		.ai_socktype = socktype,
30 	};
31 	struct addrinfo *res = NULL;
32 	char addr_str[INET6_ADDRSTRLEN] = {0};
33 	int rv;
34 
35 	/* Perform DNS query */
36 	rv = getaddrinfo(host, NULL, &hints, &res);
37 	if (rv < 0) {
38 		LOG_ERR("getaddrinfo failed (%d, errno %d)", rv, errno);
39 		return rv;
40 	}
41 	/* Store the first result */
42 	*addr = *res->ai_addr;
43 	*addrlen = res->ai_addrlen;
44 	/* Free the allocated memory */
45 	freeaddrinfo(res);
46 	/* Store the port */
47 	net_sin(addr)->sin_port = htons(port);
48 	/* Print the found address */
49 	inet_ntop(addr->sa_family, &net_sin(addr)->sin_addr, addr_str, sizeof(addr_str));
50 	LOG_INF("%s -> %s", host, addr_str);
51 	return 0;
52 }
53 
sntp_service_handler(struct net_socket_service_event * pev)54 static void sntp_service_handler(struct net_socket_service_event *pev)
55 {
56 	struct sntp_time s_time;
57 	int rc;
58 
59 	/* Read the response from the socket */
60 	rc = sntp_read_async(pev, &s_time);
61 	if (rc != 0) {
62 		LOG_ERR("Failed to read SNTP response (%d)", rc);
63 		return;
64 	}
65 
66 	/* Close the service */
67 	sntp_close_async(&service_sntp_async);
68 
69 	LOG_INF("SNTP Time: %llu (async)", s_time.seconds);
70 
71 	/* Notify test thread */
72 	k_sem_give(&sntp_async_received);
73 }
74 
do_sntp(int family)75 static void do_sntp(int family)
76 {
77 	char *family_str = family == AF_INET ? "IPv4" : "IPv6";
78 	struct sntp_time s_time;
79 	struct sntp_ctx ctx;
80 	struct sockaddr addr;
81 	socklen_t addrlen;
82 	int rv;
83 
84 	/* Get SNTP server */
85 	rv = dns_query(CONFIG_NET_SAMPLE_SNTP_SERVER_ADDRESS, CONFIG_NET_SAMPLE_SNTP_SERVER_PORT,
86 				   family, SOCK_DGRAM, &addr, &addrlen);
87 	if (rv != 0) {
88 		LOG_ERR("Failed to lookup %s SNTP server (%d)", family_str, rv);
89 		return;
90 	}
91 
92 	rv = sntp_init(&ctx, &addr, addrlen);
93 	if (rv < 0) {
94 		LOG_ERR("Failed to init SNTP %s ctx: %d", family_str, rv);
95 		goto end;
96 	}
97 
98 	LOG_INF("Sending SNTP %s request...", family_str);
99 	rv = sntp_query(&ctx, 4 * MSEC_PER_SEC, &s_time);
100 	if (rv < 0) {
101 		LOG_ERR("SNTP %s request failed: %d", family_str, rv);
102 		goto end;
103 	}
104 
105 	LOG_INF("SNTP Time: %llu", s_time.seconds);
106 
107 	sntp_close(&ctx);
108 
109 	rv = sntp_init_async(&ctx, &addr, addrlen, &service_sntp_async);
110 	if (rv < 0) {
111 		LOG_ERR("Failed to initialise SNTP context (%d)", rv);
112 		goto end;
113 	}
114 
115 	rv = sntp_send_async(&ctx);
116 	if (rv < 0) {
117 		LOG_ERR("Failed to send SNTP query (%d)", rv);
118 		goto end;
119 	}
120 
121 	/* Wait for the response to be received asynchronously */
122 	rv = k_sem_take(&sntp_async_received, K_MSEC(CONFIG_NET_SAMPLE_SNTP_SERVER_TIMEOUT_MS));
123 	if (rv < 0) {
124 		LOG_INF("SNTP response timed out (%d)", rv);
125 	}
126 
127 end:
128 	sntp_close(&ctx);
129 }
130 
main(void)131 int main(void)
132 {
133 	wait_for_network();
134 
135 	do_sntp(AF_INET);
136 
137 #if defined(CONFIG_NET_IPV6)
138 	do_sntp(AF_INET6);
139 #endif
140 
141 	return 0;
142 }
143