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