Skip to content

Commit c84299e

Browse files
committed
Encapsulate the complete logic for providing time necessary for certificate verification within the class TimeService
1 parent 278b0eb commit c84299e

File tree

5 files changed

+287
-90
lines changed

5 files changed

+287
-90
lines changed

src/ArduinoIoTCloudTCP.cpp

+5-10
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#ifdef HAS_TCP
2020
#include <ArduinoIoTCloudTCP.h>
21+
#include "utility/TimeService.h"
2122
#ifdef BOARD_HAS_ECCX08
2223
#include "utility/ECCX08Cert.h"
2324
#include "utility/BearSSLTrustAnchor.h"
@@ -29,6 +30,8 @@
2930
RTCZero rtc;
3031
#endif
3132

33+
TimeService time_service;
34+
3235
#ifdef BOARD_HAS_ECCX08
3336
const static int keySlot = 0;
3437
const static int compressedCertSlot = 10;
@@ -41,16 +44,7 @@ const static int CONNECT_FAILURE = 0;
4144
const static int CONNECT_FAILURE_SUBSCRIBE = -1;
4245

4346
static unsigned long getTime() {
44-
if (!ArduinoCloud.getConnection()) {
45-
return 0;
46-
}
47-
TcpIpConnectionHandler * connection = ArduinoCloud.getConnection();
48-
unsigned long time = connection->getTime();
49-
Debug.print(DBG_DEBUG, "NTP time: %lu", time);
50-
if (!NTPUtils::isTimeValid(time)) {
51-
Debug.print(DBG_ERROR, "Bogus NTP time from API, fallback to UDP method");
52-
time = NTPUtils(connection->getUDP()).getTime();
53-
}
47+
unsigned long const time = time_service.getTime();
5448
#ifdef ARDUINO_ARCH_SAMD
5549
rtc.setEpoch(time);
5650
#endif
@@ -92,6 +86,7 @@ int ArduinoIoTCloudTCP::begin(TcpIpConnectionHandler & connection, String broker
9286
_connection = &connection;
9387
_brokerAddress = brokerAddress;
9488
_brokerPort = brokerPort;
89+
time_service.begin(&connection);
9590
#ifdef ARDUINO_ARCH_SAMD
9691
rtc.begin();
9792
#endif

src/utility/NTPUtils.cpp

+64-67
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,80 @@
1-
#include "NTPUtils.h"
2-
#include "Arduino.h"
31
/*
4-
This Utility Class is derived from the example code found here https://www.arduino.cc/en/Tutorial/UdpNTPClient
5-
For more information on NTP (Network Time Protocol) you can refer to this Wikipedia article https://en.wikipedia.org/wiki/Network_Time_Protocol
6-
*/
7-
2+
This file is part of ArduinoIoTCloud.
83
9-
// could be a constexpr in C++14
10-
static time_t cvt_TIME(char const *time) {
11-
char s_month[5];
12-
int month, day, year;
13-
struct tm t = {0 /* tm_sec */,
14-
0 /* tm_min */,
15-
0 /* tm_hour */,
16-
0 /* tm_mday */,
17-
0 /* tm_mon */,
18-
0 /* tm_year */,
19-
0 /* tm_wday */,
20-
0 /* tm_yday */,
21-
0 /* tm_isdst */
22-
};
23-
static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
4+
Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
245
25-
sscanf(time, "%s %d %d", s_month, &day, &year);
26-
27-
month = (strstr(month_names, s_month) - month_names) / 3;
28-
29-
t.tm_mon = month;
30-
t.tm_mday = day;
31-
t.tm_year = year - 1900;
32-
t.tm_isdst = -1;
33-
34-
return mktime(&t);
35-
}
6+
This software is released under the GNU General Public License version 3,
7+
which covers the main part of arduino-cli.
8+
The terms of this license can be found at:
9+
https://www.gnu.org/licenses/gpl-3.0.en.html
3610
11+
You can be released from the requirements of the above licenses by purchasing
12+
a commercial license. Buying such a license is mandatory if you want to modify or
13+
otherwise use the software for commercial activities involving the Arduino
14+
software without disclosing the source code of your own applications. To purchase
15+
a commercial license, send an email to [email protected].
16+
*/
3717

38-
NTPUtils::NTPUtils(UDP& Udp) : Udp(Udp) {}
18+
/**************************************************************************************
19+
* INCLUDE
20+
**************************************************************************************/
3921

40-
bool NTPUtils::isTimeValid(unsigned long time) {
41-
return (time > static_cast<unsigned long>(cvt_TIME(__DATE__)));
42-
}
22+
#include "NTPUtils.h"
23+
#include "Arduino.h"
4324

44-
void NTPUtils::sendNTPpacket(uint8_t* packetBuffer) {
45-
memset(packetBuffer, 0, NTP_PACKET_SIZE);
46-
packetBuffer[0] = 0b11100011;
47-
packetBuffer[1] = 0;
48-
packetBuffer[2] = 6;
49-
packetBuffer[3] = 0xEC;
50-
packetBuffer[12] = 49;
51-
packetBuffer[13] = 0x4E;
52-
packetBuffer[14] = 49;
53-
packetBuffer[15] = 52;
54-
Udp.beginPacket(timeServer, 123);
55-
Udp.write(packetBuffer, NTP_PACKET_SIZE);
56-
Udp.endPacket();
57-
}
25+
/**************************************************************************************
26+
* PUBLIC MEMBER FUNCTIONS
27+
**************************************************************************************/
5828

59-
unsigned long NTPUtils::getTime() {
29+
unsigned long NTPUtils::getTime(UDP & udp)
30+
{
31+
udp.begin(NTP_LOCAL_PORT);
32+
33+
sendNTPpacket(udp);
6034

61-
unsigned int localPort = 8888;
62-
uint8_t packetBuffer[NTP_PACKET_SIZE];
35+
bool is_timeout = false;
36+
unsigned long const start = millis();
37+
do
38+
{
39+
is_timeout = (millis() - start) >= NTP_TIMEOUT_MS;
40+
} while(!is_timeout && !udp.parsePacket());
6341

64-
Udp.begin(localPort);
65-
sendNTPpacket(packetBuffer);
66-
long start = millis();
67-
while (!Udp.parsePacket() && (millis() - start < 10000)) {}
68-
if (millis() - start >= 1000) {
69-
//timeout reached
70-
Udp.stop();
42+
if(is_timeout) {
43+
udp.stop();
7144
return 0;
7245
}
73-
Udp.read(packetBuffer, NTP_PACKET_SIZE);
46+
47+
uint8_t ntp_packet_buf[NTP_PACKET_SIZE];
48+
udp.read(ntp_packet_buf, NTP_PACKET_SIZE);
49+
udp.stop();
7450

75-
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
76-
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
77-
unsigned long secsSince1900 = highWord << 16 | lowWord;
78-
const unsigned long seventyYears = 2208988800UL;
79-
unsigned long epoch = secsSince1900 - seventyYears;
51+
unsigned long const highWord = word(ntp_packet_buf[40], ntp_packet_buf[41]);
52+
unsigned long const lowWord = word(ntp_packet_buf[42], ntp_packet_buf[43]);
53+
unsigned long const secsSince1900 = highWord << 16 | lowWord;
54+
unsigned long const seventyYears = 2208988800UL;
55+
unsigned long const epoch = secsSince1900 - seventyYears;
8056

81-
Udp.stop();
8257
return epoch;
8358
}
59+
60+
/**************************************************************************************
61+
* PRIVATE MEMBER FUNCTIONS
62+
**************************************************************************************/
63+
64+
void NTPUtils::sendNTPpacket(UDP & udp)
65+
{
66+
uint8_t ntp_packet_buf[NTP_PACKET_SIZE] = {0};
67+
68+
ntp_packet_buf[0] = 0b11100011;
69+
ntp_packet_buf[1] = 0;
70+
ntp_packet_buf[2] = 6;
71+
ntp_packet_buf[3] = 0xEC;
72+
ntp_packet_buf[12] = 49;
73+
ntp_packet_buf[13] = 0x4E;
74+
ntp_packet_buf[14] = 49;
75+
ntp_packet_buf[15] = 52;
76+
77+
udp.beginPacket(NTP_TIME_SERVER, NTP_TIME_SERVER_PORT);
78+
udp.write(ntp_packet_buf, NTP_PACKET_SIZE);
79+
udp.endPacket();
80+
}

src/utility/NTPUtils.h

+44-13
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,54 @@
1+
/*
2+
This file is part of ArduinoIoTCloud.
3+
4+
Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
5+
6+
This software is released under the GNU General Public License version 3,
7+
which covers the main part of arduino-cli.
8+
The terms of this license can be found at:
9+
https://www.gnu.org/licenses/gpl-3.0.en.html
10+
11+
You can be released from the requirements of the above licenses by purchasing
12+
a commercial license. Buying such a license is mandatory if you want to modify or
13+
otherwise use the software for commercial activities involving the Arduino
14+
software without disclosing the source code of your own applications. To purchase
15+
a commercial license, send an email to [email protected].
16+
*/
17+
118
#ifndef __NTP_UTILS__
219
#define __NTP_UTILS__
20+
321
/*
422
This Utility Class is derived from the example code found here https://www.arduino.cc/en/Tutorial/UdpNTPClient
523
For more information on NTP (Network Time Protocol) you can refer to this Wikipedia article https://en.wikipedia.org/wiki/Network_Time_Protocol
624
*/
725

8-
#include "Udp.h"
9-
#include <time.h>
10-
11-
class NTPUtils {
12-
public:
13-
NTPUtils(UDP& Udp);
14-
void sendNTPpacket(uint8_t* packetBuffer);
15-
unsigned long getTime();
16-
static bool isTimeValid(unsigned long time);
17-
private:
18-
const char* timeServer = "time.arduino.cc";
19-
const int NTP_PACKET_SIZE = 48;
20-
UDP& Udp;
26+
/**************************************************************************************
27+
* INCLUDE
28+
**************************************************************************************/
29+
30+
#include <Udp.h>
31+
32+
/**************************************************************************************
33+
* CLASS DECLARATION
34+
**************************************************************************************/
35+
36+
class NTPUtils
37+
{
38+
public:
39+
40+
static unsigned long getTime(UDP & udp);
41+
42+
private:
43+
44+
static size_t const NTP_PACKET_SIZE = 48;
45+
static int const NTP_TIME_SERVER_PORT = 123;
46+
static int const NTP_LOCAL_PORT = 8888;
47+
static unsigned long const NTP_TIMEOUT_MS = 1000;
48+
static char constexpr * NTP_TIME_SERVER = "time.arduino.cc";
49+
50+
static void sendNTPpacket(UDP & udp);
51+
2152
};
2253

2354
#endif

src/utility/TimeService.cpp

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
This file is part of ArduinoIoTCloud.
3+
4+
Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
5+
6+
This software is released under the GNU General Public License version 3,
7+
which covers the main part of arduino-cli.
8+
The terms of this license can be found at:
9+
https://www.gnu.org/licenses/gpl-3.0.en.html
10+
11+
You can be released from the requirements of the above licenses by purchasing
12+
a commercial license. Buying such a license is mandatory if you want to modify or
13+
otherwise use the software for commercial activities involving the Arduino
14+
software without disclosing the source code of your own applications. To purchase
15+
a commercial license, send an email to [email protected].
16+
*/
17+
18+
/**************************************************************************************
19+
* INCLUDE
20+
**************************************************************************************/
21+
22+
#include "TimeService.h"
23+
24+
#include <time.h>
25+
26+
#include "NTPUtils.h"
27+
28+
/**************************************************************************************
29+
* INTERNAL FUNCTION DECLARATION
30+
**************************************************************************************/
31+
32+
time_t cvt_time(char const * time);
33+
34+
/**************************************************************************************
35+
* CONSTANTS
36+
**************************************************************************************/
37+
38+
static time_t const EPOCH_AT_COMPILE_TIME = cvt_time(__DATE__);
39+
40+
/**************************************************************************************
41+
* CTOR/DTOR
42+
**************************************************************************************/
43+
44+
TimeService::TimeService()
45+
: _con_hdl(nullptr)
46+
{
47+
48+
}
49+
50+
/**************************************************************************************
51+
* PUBLIC MEMBER FUNCTIONS
52+
**************************************************************************************/
53+
54+
void TimeService::begin(ConnectionHandler * con_hdl)
55+
{
56+
_con_hdl = con_hdl;
57+
}
58+
59+
unsigned long TimeService::getTime()
60+
{
61+
if(_con_hdl == nullptr) return 0;
62+
63+
/* At first try to see if a valid time can be obtained
64+
* using the network time available via the connection
65+
* handler.
66+
*/
67+
unsigned long const connection_time = _con_hdl->getTime();
68+
if(isTimeValid(connection_time)) {
69+
return connection_time;
70+
}
71+
72+
/* If no valid network time is available try to obtain the
73+
* time via NTP next.
74+
*/
75+
unsigned long const ntp_time = NTPUtils::getTime(_con_hdl->getUDP());
76+
if(isTimeValid(ntp_time)) {
77+
return ntp_time;
78+
}
79+
80+
return 0;
81+
}
82+
83+
/**************************************************************************************
84+
* PRIVATE MEMBER FUNCTIONS
85+
**************************************************************************************/
86+
87+
bool TimeService::isTimeValid(unsigned long const time)
88+
{
89+
return (time >= EPOCH_AT_COMPILE_TIME);
90+
}
91+
92+
/**************************************************************************************
93+
* INTERNAL FUNCTION DEFINITION
94+
**************************************************************************************/
95+
96+
time_t cvt_time(char const * time)
97+
{
98+
char s_month[5];
99+
int month, day, year;
100+
struct tm t =
101+
{
102+
0 /* tm_sec */,
103+
0 /* tm_min */,
104+
0 /* tm_hour */,
105+
0 /* tm_mday */,
106+
0 /* tm_mon */,
107+
0 /* tm_year */,
108+
0 /* tm_wday */,
109+
0 /* tm_yday */,
110+
0 /* tm_isdst */
111+
};
112+
static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
113+
114+
sscanf(time, "%s %d %d", s_month, &day, &year);
115+
116+
month = (strstr(month_names, s_month) - month_names) / 3;
117+
118+
t.tm_mon = month;
119+
t.tm_mday = day;
120+
t.tm_year = year - 1900;
121+
t.tm_isdst = -1;
122+
123+
return mktime(&t);
124+
}

0 commit comments

Comments
 (0)