User | Revision | Line number | New contents of line |
donatien |
0:9fa20c138e48
|
1
|
/* NTPClient.cpp */
|
donatien |
0:9fa20c138e48
|
2
|
/*
|
donatien |
0:9fa20c138e48
|
3
|
Copyright (C) 2012 ARM Limited.
|
donatien |
0:9fa20c138e48
|
4
|
|
donatien |
0:9fa20c138e48
|
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
donatien |
0:9fa20c138e48
|
6
|
this software and associated documentation files (the "Software"), to deal in
|
donatien |
0:9fa20c138e48
|
7
|
the Software without restriction, including without limitation the rights to
|
donatien |
0:9fa20c138e48
|
8
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
donatien |
0:9fa20c138e48
|
9
|
of the Software, and to permit persons to whom the Software is furnished to do
|
donatien |
0:9fa20c138e48
|
10
|
so, subject to the following conditions:
|
donatien |
0:9fa20c138e48
|
11
|
|
donatien |
0:9fa20c138e48
|
12
|
The above copyright notice and this permission notice shall be included in all
|
donatien |
0:9fa20c138e48
|
13
|
copies or substantial portions of the Software.
|
donatien |
0:9fa20c138e48
|
14
|
|
donatien |
0:9fa20c138e48
|
15
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
donatien |
0:9fa20c138e48
|
16
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
donatien |
0:9fa20c138e48
|
17
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
donatien |
0:9fa20c138e48
|
18
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
donatien |
0:9fa20c138e48
|
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
donatien |
0:9fa20c138e48
|
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
donatien |
0:9fa20c138e48
|
21
|
SOFTWARE.
|
donatien |
0:9fa20c138e48
|
22
|
*/
|
donatien |
0:9fa20c138e48
|
23
|
|
donatien |
0:9fa20c138e48
|
24
|
#define __DEBUG__ 4 //Maximum verbosity
|
donatien |
0:9fa20c138e48
|
25
|
#ifndef __MODULE__
|
donatien |
0:9fa20c138e48
|
26
|
#define __MODULE__ "NTPClient.cpp"
|
donatien |
0:9fa20c138e48
|
27
|
#endif
|
donatien |
0:9fa20c138e48
|
28
|
|
donatien |
0:9fa20c138e48
|
29
|
#include "core/fwk.h"
|
donatien |
0:9fa20c138e48
|
30
|
|
donatien |
0:9fa20c138e48
|
31
|
#include "NTPClient.h"
|
donatien |
0:9fa20c138e48
|
32
|
|
donatien |
0:9fa20c138e48
|
33
|
#include "mbed.h" //time() and set_time()
|
donatien |
0:9fa20c138e48
|
34
|
|
donatien |
0:9fa20c138e48
|
35
|
#define NTP_PORT 123
|
donatien |
0:9fa20c138e48
|
36
|
#define NTP_CLIENT_PORT 0 //Random port
|
donatien |
0:9fa20c138e48
|
37
|
#define NTP_REQUEST_TIMEOUT 15000
|
donatien |
0:9fa20c138e48
|
38
|
#define NTP_TIMESTAMP_DELTA 2208988800ull //Diff btw a UNIX timestamp (Starting Jan, 1st 1970) and a NTP timestamp (Starting Jan, 1st 1900)
|
donatien |
0:9fa20c138e48
|
39
|
|
donatien |
0:9fa20c138e48
|
40
|
NTPClient::NTPClient()
|
donatien |
0:9fa20c138e48
|
41
|
{
|
donatien |
0:9fa20c138e48
|
42
|
|
donatien |
0:9fa20c138e48
|
43
|
|
donatien |
0:9fa20c138e48
|
44
|
}
|
donatien |
0:9fa20c138e48
|
45
|
|
donatien |
0:9fa20c138e48
|
46
|
int NTPClient::setTime(const char* host, uint16_t port, uint32_t timeout)
|
donatien |
0:9fa20c138e48
|
47
|
{
|
donatien |
0:9fa20c138e48
|
48
|
struct sockaddr_in serverAddr;
|
donatien |
0:9fa20c138e48
|
49
|
|
donatien |
0:9fa20c138e48
|
50
|
std::memset(&serverAddr, 0, sizeof(struct sockaddr_in));
|
donatien |
0:9fa20c138e48
|
51
|
|
donatien |
0:9fa20c138e48
|
52
|
#if __DEBUG__ >= 3
|
donatien |
0:9fa20c138e48
|
53
|
time_t ctTime;
|
donatien |
0:9fa20c138e48
|
54
|
ctTime = time(NULL);
|
donatien |
0:9fa20c138e48
|
55
|
INFO("Time is set to (UTC): %s", ctime(&ctTime));
|
donatien |
0:9fa20c138e48
|
56
|
#endif
|
donatien |
0:9fa20c138e48
|
57
|
|
donatien |
0:9fa20c138e48
|
58
|
//Resolve DNS if needed
|
donatien |
0:9fa20c138e48
|
59
|
if(serverAddr.sin_addr.s_addr == 0)
|
donatien |
0:9fa20c138e48
|
60
|
{
|
donatien |
0:9fa20c138e48
|
61
|
DBG("Resolving DNS address or populate hard-coded IP address");
|
donatien |
0:9fa20c138e48
|
62
|
struct hostent *server = socket::gethostbyname(host);
|
donatien |
0:9fa20c138e48
|
63
|
if(server == NULL)
|
donatien |
0:9fa20c138e48
|
64
|
{
|
donatien |
0:9fa20c138e48
|
65
|
return NET_NOTFOUND; //Fail
|
donatien |
0:9fa20c138e48
|
66
|
}
|
donatien |
0:9fa20c138e48
|
67
|
memcpy((char*)&serverAddr.sin_addr.s_addr, (char*)server->h_addr_list[0], server->h_length);
|
donatien |
0:9fa20c138e48
|
68
|
}
|
donatien |
0:9fa20c138e48
|
69
|
|
donatien |
0:9fa20c138e48
|
70
|
serverAddr.sin_family = AF_INET;
|
donatien |
0:9fa20c138e48
|
71
|
serverAddr.sin_port = htons(port);
|
donatien |
0:9fa20c138e48
|
72
|
|
donatien |
0:9fa20c138e48
|
73
|
//Now create & bind socket
|
donatien |
0:9fa20c138e48
|
74
|
DBG("Creating socket");
|
donatien |
0:9fa20c138e48
|
75
|
int sock = socket::socket(AF_INET, SOCK_DGRAM, 0); //UDP socket
|
donatien |
0:9fa20c138e48
|
76
|
if (sock < 0)
|
donatien |
0:9fa20c138e48
|
77
|
{
|
donatien |
0:9fa20c138e48
|
78
|
ERR("Could not create socket");
|
donatien |
0:9fa20c138e48
|
79
|
return NET_OOM;
|
donatien |
0:9fa20c138e48
|
80
|
}
|
donatien |
0:9fa20c138e48
|
81
|
DBG("Handle is %d",sock);
|
donatien |
0:9fa20c138e48
|
82
|
|
donatien |
0:9fa20c138e48
|
83
|
//Create local address
|
donatien |
0:9fa20c138e48
|
84
|
struct sockaddr_in localhostAddr;
|
donatien |
0:9fa20c138e48
|
85
|
|
donatien |
0:9fa20c138e48
|
86
|
std::memset(&localhostAddr, 0, sizeof(struct sockaddr_in));
|
donatien |
0:9fa20c138e48
|
87
|
|
donatien |
0:9fa20c138e48
|
88
|
localhostAddr.sin_family = AF_INET;
|
donatien |
0:9fa20c138e48
|
89
|
localhostAddr.sin_port = htons(NTP_CLIENT_PORT); //Random port
|
donatien |
0:9fa20c138e48
|
90
|
localhostAddr.sin_addr.s_addr = htonl(INADDR_ANY); //Any local address
|
donatien |
0:9fa20c138e48
|
91
|
|
donatien |
0:9fa20c138e48
|
92
|
if ( socket::bind( sock, (struct sockaddr*)&localhostAddr, sizeof(localhostAddr)) < 0 ) //Listen on local port
|
donatien |
0:9fa20c138e48
|
93
|
{
|
donatien |
0:9fa20c138e48
|
94
|
ERR("Could not bind socket");
|
donatien |
0:9fa20c138e48
|
95
|
socket::close(sock);
|
donatien |
0:9fa20c138e48
|
96
|
return NET_OOM;
|
donatien |
0:9fa20c138e48
|
97
|
}
|
donatien |
0:9fa20c138e48
|
98
|
|
donatien |
0:9fa20c138e48
|
99
|
struct NTPPacket pkt;
|
donatien |
0:9fa20c138e48
|
100
|
|
donatien |
0:9fa20c138e48
|
101
|
//Now ping the server and wait for response
|
donatien |
0:9fa20c138e48
|
102
|
DBG("Ping");
|
donatien |
0:9fa20c138e48
|
103
|
//Prepare NTP Packet:
|
donatien |
0:9fa20c138e48
|
104
|
pkt.li = 0; //Leap Indicator : No warning
|
donatien |
0:9fa20c138e48
|
105
|
pkt.vn = 4; //Version Number : 4
|
donatien |
0:9fa20c138e48
|
106
|
pkt.mode = 3; //Client mode
|
donatien |
0:9fa20c138e48
|
107
|
pkt.stratum = 0; //Not relevant here
|
donatien |
0:9fa20c138e48
|
108
|
pkt.poll = 0; //Not significant as well
|
donatien |
0:9fa20c138e48
|
109
|
pkt.precision = 0; //Neither this one is
|
donatien |
0:9fa20c138e48
|
110
|
|
donatien |
0:9fa20c138e48
|
111
|
pkt.rootDelay = 0; //Or this one
|
donatien |
0:9fa20c138e48
|
112
|
pkt.rootDispersion = 0; //Or that one
|
donatien |
0:9fa20c138e48
|
113
|
pkt.refId = 0; //...
|
donatien |
0:9fa20c138e48
|
114
|
|
donatien |
0:9fa20c138e48
|
115
|
pkt.refTm_s = 0;
|
donatien |
0:9fa20c138e48
|
116
|
pkt.origTm_s = 0;
|
donatien |
0:9fa20c138e48
|
117
|
pkt.rxTm_s = 0;
|
donatien |
0:9fa20c138e48
|
118
|
pkt.txTm_s = htonl( NTP_TIMESTAMP_DELTA + time(NULL) ); //WARN: We are in LE format, network byte order is BE
|
donatien |
0:9fa20c138e48
|
119
|
|
donatien |
0:9fa20c138e48
|
120
|
pkt.refTm_f = pkt.origTm_f = pkt.rxTm_f = pkt.txTm_f = 0;
|
donatien |
0:9fa20c138e48
|
121
|
|
donatien |
0:9fa20c138e48
|
122
|
//Set timeout, non-blocking and wait using select
|
donatien |
0:9fa20c138e48
|
123
|
if( socket::sendto( sock, (void*)&pkt, sizeof(NTPPacket), 0, (struct sockaddr*)&serverAddr, sizeof(serverAddr) ) < 0 )
|
donatien |
0:9fa20c138e48
|
124
|
{
|
donatien |
0:9fa20c138e48
|
125
|
ERR("Could not send packet");
|
donatien |
0:9fa20c138e48
|
126
|
socket::close(sock);
|
donatien |
0:9fa20c138e48
|
127
|
return NET_CONN;
|
donatien |
0:9fa20c138e48
|
128
|
}
|
donatien |
0:9fa20c138e48
|
129
|
|
donatien |
0:9fa20c138e48
|
130
|
//Wait for socket to be readable
|
donatien |
0:9fa20c138e48
|
131
|
//Creating FS set
|
donatien |
0:9fa20c138e48
|
132
|
fd_set socksSet;
|
donatien |
0:9fa20c138e48
|
133
|
FD_ZERO(&socksSet);
|
donatien |
0:9fa20c138e48
|
134
|
FD_SET(sock, &socksSet);
|
donatien |
0:9fa20c138e48
|
135
|
struct timeval t_val;
|
donatien |
0:9fa20c138e48
|
136
|
t_val.tv_sec = timeout / 1000;
|
donatien |
0:9fa20c138e48
|
137
|
t_val.tv_usec = (timeout - (t_val.tv_sec * 1000)) * 1000;
|
donatien |
0:9fa20c138e48
|
138
|
int ret = socket::select(FD_SETSIZE, &socksSet, NULL, NULL, &t_val);
|
donatien |
0:9fa20c138e48
|
139
|
if(ret <= 0 || !FD_ISSET(sock, &socksSet))
|
donatien |
0:9fa20c138e48
|
140
|
{
|
donatien |
0:9fa20c138e48
|
141
|
ERR("Timeout while waiting for answer");
|
donatien |
0:9fa20c138e48
|
142
|
socket::close(sock);
|
donatien |
0:9fa20c138e48
|
143
|
return NET_TIMEOUT; //Timeout
|
donatien |
0:9fa20c138e48
|
144
|
}
|
donatien |
0:9fa20c138e48
|
145
|
|
donatien |
0:9fa20c138e48
|
146
|
//Read response
|
donatien |
0:9fa20c138e48
|
147
|
DBG("Pong");
|
donatien |
0:9fa20c138e48
|
148
|
struct sockaddr_in respAddr;
|
donatien |
0:9fa20c138e48
|
149
|
socklen_t respAddrLen = sizeof(respAddr);
|
donatien |
0:9fa20c138e48
|
150
|
do
|
donatien |
0:9fa20c138e48
|
151
|
{
|
donatien |
0:9fa20c138e48
|
152
|
ret = socket::recvfrom( sock, (void*)&pkt, sizeof(NTPPacket), 0, (struct sockaddr*)&respAddr, &respAddrLen);
|
donatien |
0:9fa20c138e48
|
153
|
if(ret < 0)
|
donatien |
0:9fa20c138e48
|
154
|
{
|
donatien |
0:9fa20c138e48
|
155
|
ERR("Could not receive packet");
|
donatien |
0:9fa20c138e48
|
156
|
socket::close(sock);
|
donatien |
0:9fa20c138e48
|
157
|
return NET_CONN;
|
donatien |
0:9fa20c138e48
|
158
|
}
|
donatien |
0:9fa20c138e48
|
159
|
} while( respAddr.sin_addr.s_addr != serverAddr.sin_addr.s_addr);
|
donatien |
0:9fa20c138e48
|
160
|
|
donatien |
0:9fa20c138e48
|
161
|
if(ret < sizeof(NTPPacket)) //TODO: Accept chunks
|
donatien |
0:9fa20c138e48
|
162
|
{
|
donatien |
0:9fa20c138e48
|
163
|
ERR("Receive packet size does not match");
|
donatien |
0:9fa20c138e48
|
164
|
socket::close(sock);
|
donatien |
0:9fa20c138e48
|
165
|
return NET_PROTOCOL;
|
donatien |
0:9fa20c138e48
|
166
|
}
|
donatien |
0:9fa20c138e48
|
167
|
|
donatien |
0:9fa20c138e48
|
168
|
if( pkt.stratum == 0) //Kiss of death message : Not good !
|
donatien |
0:9fa20c138e48
|
169
|
{
|
donatien |
0:9fa20c138e48
|
170
|
ERR("Kissed to death!");
|
donatien |
0:9fa20c138e48
|
171
|
socket::close(sock);
|
donatien |
0:9fa20c138e48
|
172
|
return NTP_PORT;
|
donatien |
0:9fa20c138e48
|
173
|
}
|
donatien |
0:9fa20c138e48
|
174
|
|
donatien |
0:9fa20c138e48
|
175
|
//Correct Endianness
|
donatien |
0:9fa20c138e48
|
176
|
pkt.refTm_s = ntohl( pkt.refTm_s );
|
donatien |
0:9fa20c138e48
|
177
|
pkt.refTm_f = ntohl( pkt.refTm_f );
|
donatien |
0:9fa20c138e48
|
178
|
pkt.origTm_s = ntohl( pkt.origTm_s );
|
donatien |
0:9fa20c138e48
|
179
|
pkt.origTm_f = ntohl( pkt.origTm_f );
|
donatien |
0:9fa20c138e48
|
180
|
pkt.rxTm_s = ntohl( pkt.rxTm_s );
|
donatien |
0:9fa20c138e48
|
181
|
pkt.rxTm_f = ntohl( pkt.rxTm_f );
|
donatien |
0:9fa20c138e48
|
182
|
pkt.txTm_s = ntohl( pkt.txTm_s );
|
donatien |
0:9fa20c138e48
|
183
|
pkt.txTm_f = ntohl( pkt.txTm_f );
|
donatien |
0:9fa20c138e48
|
184
|
|
donatien |
0:9fa20c138e48
|
185
|
//Compute offset, see RFC 4330 p.13
|
donatien |
0:9fa20c138e48
|
186
|
uint32_t destTm_s = (NTP_TIMESTAMP_DELTA + time(NULL));
|
donatien |
0:9fa20c138e48
|
187
|
int64_t offset = ( (int64_t)( pkt.rxTm_s - pkt.origTm_s ) + (int64_t) ( pkt.txTm_s - destTm_s ) ) / 2; //Avoid overflow
|
donatien |
0:9fa20c138e48
|
188
|
DBG("Sent @%ul", pkt.txTm_s);
|
donatien |
0:9fa20c138e48
|
189
|
DBG("Offset: %ul", offset);
|
donatien |
0:9fa20c138e48
|
190
|
//Set time accordingly
|
donatien |
0:9fa20c138e48
|
191
|
set_time( time(NULL) + offset );
|
donatien |
0:9fa20c138e48
|
192
|
|
donatien |
0:9fa20c138e48
|
193
|
#if __DEBUG__ >= 3
|
donatien |
0:9fa20c138e48
|
194
|
ctTime = time(NULL);
|
donatien |
0:9fa20c138e48
|
195
|
INFO("Time is now (UTC): %s", ctime(&ctTime));
|
donatien |
0:9fa20c138e48
|
196
|
#endif
|
donatien |
0:9fa20c138e48
|
197
|
|
donatien |
0:9fa20c138e48
|
198
|
socket::close(sock);
|
donatien |
0:9fa20c138e48
|
199
|
|
donatien |
0:9fa20c138e48
|
200
|
return OK;
|
donatien |
0:9fa20c138e48
|
201
|
}
|
donatien |
0:9fa20c138e48
|
202
|
|