time_helper.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. /* time_helper.c
  2. *
  3. * Copyright (C) 2006-2024 wolfSSL Inc.
  4. *
  5. * This file is part of wolfSSL.
  6. *
  7. * wolfSSL is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * wolfSSL is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
  20. */
  21. /* See https://tf.nist.gov/tf-cgi/servers.cgi */
  22. /* common Espressif time_helper v5.6.6.001 */
  23. #include "sdkconfig.h"
  24. #include "time_helper.h"
  25. #include <esp_log.h>
  26. #include <esp_idf_version.h>
  27. #if defined(ESP_IDF_VERSION_MAJOR) && defined(ESP_IDF_VERSION_MINOR)
  28. #if (ESP_IDF_VERSION_MAJOR == 5) && (ESP_IDF_VERSION_MINOR >= 1)
  29. #define HAS_ESP_NETIF_SNTP 1
  30. #include <lwip/apps/sntp.h>
  31. #include <esp_netif_sntp.h>
  32. #else
  33. #include <string.h>
  34. #include <esp_sntp.h>
  35. #endif
  36. #else
  37. /* TODO Consider non ESP-IDF environments */
  38. #endif
  39. /* ESP-IDF uses a 64-bit signed integer to represent time_t starting from release v5.0
  40. * See: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/system_time.html#year-2036-and-2038-overflow-issues
  41. */
  42. /* see https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html */
  43. #ifndef TIME_ZONE
  44. /*
  45. * PST represents Pacific Standard Time.
  46. * +8 specifies the offset from UTC (Coordinated Universal Time), indicating
  47. * that Pacific Time is UTC-8 during standard time.
  48. * PDT represents Pacific Daylight Time.
  49. * M3.2.0 indicates that Daylight Saving Time (DST) starts on the
  50. * second (2) Sunday (0) of March (3).
  51. * M11.1.0 indicates that DST ends on the first (1) Sunday (0) of November (11)
  52. */
  53. #define TIME_ZONE "PST+8PDT,M3.2.0,M11.1.0"
  54. #endif /* not defined: TIME_ZONE, so we are setting our own */
  55. #define NTP_RETRY_COUNT 10
  56. /* NELEMS(x) number of elements
  57. * To determine the number of elements in the array, we can divide the total
  58. * size of the array by the size of the array element.
  59. * See https://stackoverflow.com/questions/37538/how-do-i-determine-the-size-of-my-array-in-c
  60. **/
  61. #define NELEMS(x) ( (int)(sizeof(x) / sizeof((x)[0])) )
  62. /* See also CONFIG_LWIP_SNTP_MAX_SERVERS in sdkconfig */
  63. #define NTP_SERVER_LIST ( (char*[]) { \
  64. "pool.ntp.org", \
  65. "time.nist.gov", \
  66. "utcnist.colorado.edu" \
  67. } \
  68. )
  69. /* #define NTP_SERVER_COUNT using NELEMS:
  70. *
  71. * (int)(sizeof(NTP_SERVER_LIST) / sizeof(NTP_SERVER_LIST[0]))
  72. */
  73. #define NTP_SERVER_COUNT NELEMS(NTP_SERVER_LIST)
  74. #ifndef CONFIG_LWIP_SNTP_MAX_SERVERS
  75. /* We should find max value in sdkconfig, if not set it to our count:*/
  76. #define CONFIG_LWIP_SNTP_MAX_SERVERS NTP_SERVER_COUNT
  77. #endif
  78. char* ntpServerList[NTP_SERVER_COUNT] = NTP_SERVER_LIST;
  79. const static char* TAG = "time_helper";
  80. /* our NTP server list is global info */
  81. extern char* ntpServerList[NTP_SERVER_COUNT];
  82. /* Show the current date and time */
  83. int esp_show_current_datetime(void)
  84. {
  85. time_t now;
  86. char strftime_buf[64];
  87. struct tm timeinfo;
  88. time(&now);
  89. setenv("TZ", TIME_ZONE, 1);
  90. tzset();
  91. localtime_r(&now, &timeinfo);
  92. strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
  93. ESP_LOGI(TAG, "The current date/time is: %s", strftime_buf);
  94. return ESP_OK;
  95. }
  96. /* the worst-case scenario is a hard-coded date/time */
  97. int set_fixed_default_time(void)
  98. {
  99. /* ideally, we'd like to set time from network,
  100. * but let's set a default time, just in case */
  101. struct tm timeinfo = {
  102. .tm_year = 2024 - 1900,
  103. .tm_mon = 3,
  104. .tm_mday = 01,
  105. .tm_hour = 13,
  106. .tm_min = 01,
  107. .tm_sec = 05
  108. };
  109. struct timeval now;
  110. time_t interim_time;
  111. int ret = -1;
  112. /* set interim static time */
  113. interim_time = mktime(&timeinfo);
  114. ESP_LOGI(TAG, "Adjusting time from fixed value");
  115. now = (struct timeval){ .tv_sec = interim_time };
  116. ret = settimeofday(&now, NULL);
  117. ESP_LOGI(TAG, "settimeofday result = %d", ret);
  118. return ret;
  119. }
  120. /* probably_valid_time_string(s)
  121. *
  122. * some sanity checks on time string before calling sscanf()
  123. *
  124. * returns 0 == ESP_OK == Success if str is likely a valid time.
  125. * -1 == ESP_FAIL otherwise
  126. */
  127. int probably_valid_time_string(const char* str)
  128. {
  129. int ret = ESP_OK;
  130. size_t length = 0;
  131. size_t spaces = 0;
  132. size_t colons = 0;
  133. while (str[length] != '\0') {
  134. if (str[length] == ' ') {
  135. spaces++;
  136. }
  137. if (str[length] == ':') {
  138. colons++;
  139. }
  140. length++;
  141. }
  142. if ((length > 32) || (spaces < 4) || (spaces > 5) || (colons > 2)) {
  143. ret = ESP_FAIL;
  144. ESP_LOGE(TAG, "ERROR, failed time sanity check: %s", str);
  145. }
  146. return ret;
  147. }
  148. /* set_time_from_string(s)
  149. *
  150. * returns 0 = success if able to set the time from the provided string
  151. * error for any other value, typically -1 */
  152. int set_time_from_string(const char* time_buffer)
  153. {
  154. /* expecting github default formatting: 'Thu Aug 31 12:41:45 2023 -0700' */
  155. char offset[28]; /* large arrays, just in case there's still bad data */
  156. char day_str[28];
  157. char month_str[28];
  158. const char *format = "%3s %3s %d %d:%d:%d %d %s";
  159. struct tm this_timeinfo;
  160. struct timeval now;
  161. time_t interim_time;
  162. int day, year, hour, minute, second;
  163. int quote_offset = 0;
  164. int ret = 0;
  165. /* perform some basic sanity checkes */
  166. ret = probably_valid_time_string(time_buffer);
  167. if (ret == ESP_OK) {
  168. /* we are expecting the string to be encapsulated in single quotes */
  169. if (*time_buffer == 0x27) {
  170. quote_offset = 1;
  171. }
  172. ret = sscanf(time_buffer + quote_offset,
  173. format,
  174. day_str, month_str,
  175. &day, &hour, &minute, &second, &year, &offset);
  176. if (ret == 8) {
  177. /* we found a match for all componets */
  178. const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  179. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  180. };
  181. for (int i = 0; i < 12; i++) {
  182. if (strcmp(month_str, months[i]) == 0) {
  183. this_timeinfo.tm_mon = i;
  184. break;
  185. }
  186. }
  187. this_timeinfo.tm_mday = day;
  188. this_timeinfo.tm_hour = hour;
  189. this_timeinfo.tm_min = minute;
  190. this_timeinfo.tm_sec = second;
  191. this_timeinfo.tm_year = year - 1900; /* Years since 1900 */
  192. interim_time = mktime(&this_timeinfo);
  193. now = (struct timeval){ .tv_sec = interim_time };
  194. ret = settimeofday(&now, NULL);
  195. ESP_LOGI(TAG, "Time updated to %s", time_buffer);
  196. }
  197. else {
  198. ESP_LOGE(TAG, "Failed to convert \"%s\" to a tm date.",
  199. time_buffer);
  200. ESP_LOGI(TAG, "Trying fixed date that was hard-coded....");
  201. set_fixed_default_time();
  202. ret = ESP_FAIL;
  203. }
  204. }
  205. return ret;
  206. }
  207. /* set time; returns 0 if succecssfully configured with NTP */
  208. int set_time(void)
  209. {
  210. #ifndef NTP_SERVER_COUNT
  211. ESP_LOGW(TAG, "Warning: no sntp server names defined. "
  212. "Setting to empty list");
  213. #define NTP_SERVER_COUNT 0
  214. #warning "NTP not properly configured"
  215. #endif /* not defined: NTP_SERVER_COUNT */
  216. #ifdef HAS_ESP_NETIF_SNTP
  217. #if CONFIG_LWIP_SNTP_MAX_SERVERS > 1
  218. esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG_MULTIPLE(
  219. NTP_SERVER_COUNT,
  220. ESP_SNTP_SERVER_LIST(ntpServerList[0])
  221. );
  222. #else
  223. esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG(ntpServerList[0]);
  224. #endif /* CONFIG_LWIP_SNTP_MAX_SERVERS > 1 */
  225. #endif /* HAS_ESP_NETIF_SNTP */
  226. int ret = 0;
  227. int i = 0; /* counter for time servers */
  228. ESP_LOGI(TAG, "Setting the time. Startup time:");
  229. esp_show_current_datetime();
  230. #ifdef LIBWOLFSSL_VERSION_GIT_HASH_DATE
  231. /* initialy set a default approximate time from recent git commit */
  232. ESP_LOGI(TAG, "Found git hash date, attempting to set system date: %s",
  233. LIBWOLFSSL_VERSION_GIT_HASH_DATE);
  234. set_time_from_string(LIBWOLFSSL_VERSION_GIT_HASH_DATE"\0");
  235. esp_show_current_datetime();
  236. ret = -4;
  237. #else
  238. /* otherwise set a fixed time that was hard coded */
  239. set_fixed_default_time();
  240. esp_show_current_datetime();
  241. ret = -3;
  242. #endif
  243. #ifdef CONFIG_SNTP_TIME_SYNC_METHOD_SMOOTH
  244. config.smooth_sync = true;
  245. #endif
  246. if (NTP_SERVER_COUNT) {
  247. /* next, let's setup NTP time servers
  248. *
  249. * see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/system_time.html#sntp-time-synchronization
  250. *
  251. * WARNING: do not set operating mode while SNTP client is running!
  252. */
  253. /* TODO Consider esp_sntp_setoperatingmode(SNTP_OPMODE_POLL); */
  254. sntp_setoperatingmode(SNTP_OPMODE_POLL);
  255. if (NTP_SERVER_COUNT > CONFIG_LWIP_SNTP_MAX_SERVERS) {
  256. ESP_LOGW(TAG, "WARNING: %d NTP Servers defined, but "
  257. "CONFIG_LWIP_SNTP_MAX_SERVERS = %d",
  258. NTP_SERVER_COUNT,CONFIG_LWIP_SNTP_MAX_SERVERS);
  259. }
  260. ESP_LOGI(TAG, "sntp_setservername:");
  261. for (i = 0; i < CONFIG_LWIP_SNTP_MAX_SERVERS; i++) {
  262. const char* thisServer = ntpServerList[i];
  263. if (strncmp(thisServer, "\x00", 1) == 0) {
  264. /* just in case we run out of NTP servers */
  265. break;
  266. }
  267. ESP_LOGI(TAG, "%s", thisServer);
  268. sntp_setservername(i, thisServer);
  269. ret = ESP_OK;
  270. }
  271. #ifdef HAS_ESP_NETIF_SNTP
  272. ret = esp_netif_sntp_init(&config);
  273. #else
  274. ESP_LOGW(TAG,"Warning: Consider upgrading ESP-IDF to take advantage "
  275. "of updated SNTP libraries");
  276. #endif
  277. if (ret == ESP_OK) {
  278. ESP_LOGV(TAG, "Successfully called esp_netif_sntp_init");
  279. }
  280. else {
  281. ESP_LOGE(TAG, "ERROR: esp_netif_sntp_init return = %d", ret);
  282. }
  283. sntp_init();
  284. switch (ret) {
  285. case ESP_ERR_INVALID_STATE:
  286. break;
  287. default:
  288. break;
  289. }
  290. ESP_LOGI(TAG, "sntp_init done.");
  291. }
  292. else {
  293. ESP_LOGW(TAG, "No sntp time servers found.");
  294. ret = -1;
  295. }
  296. esp_show_current_datetime();
  297. ESP_LOGI(TAG, "time helper existing with result = %d", ret);
  298. return ret;
  299. }
  300. /* wait for NTP to actually set the time */
  301. int set_time_wait_for_ntp(void)
  302. {
  303. int ret = 0;
  304. #ifdef HAS_ESP_NETIF_SNTP
  305. int ntp_retry = 0;
  306. const int ntp_retry_count = NTP_RETRY_COUNT;
  307. ret = esp_netif_sntp_start();
  308. ret = esp_netif_sntp_sync_wait(500 / portTICK_PERIOD_MS);
  309. #else
  310. ESP_LOGW(TAG, "HAS_ESP_NETIF_SNTP not defined");
  311. #endif /* HAS_ESP_NETIF_SNTP */
  312. esp_show_current_datetime();
  313. #ifdef HAS_ESP_NETIF_SNTP
  314. while (ret == ESP_ERR_TIMEOUT && (ntp_retry++ < ntp_retry_count)) {
  315. ret = esp_netif_sntp_sync_wait(1000 / portTICK_PERIOD_MS);
  316. ESP_LOGI(TAG, "Waiting for NTP to sync time... (%d/%d)",
  317. ntp_retry,
  318. ntp_retry_count);
  319. esp_show_current_datetime();
  320. }
  321. #endif /* HAS_ESP_NETIF_SNTP */
  322. #ifdef TIME_ZONE
  323. setenv("TZ", TIME_ZONE, 1);
  324. tzset();
  325. #endif
  326. if (ret == ESP_OK) {
  327. ESP_LOGI(TAG, "Successfully set time via NTP servers.");
  328. }
  329. else {
  330. ESP_LOGW(TAG, "Warning: Failed to set time with NTP: "
  331. "result = 0x%0x: %s",
  332. ret, esp_err_to_name(ret));
  333. }
  334. return ret;
  335. }