NTP Client for the mbed networking libraries. The small change to this version is that there can be only one cause for the return value zero.

Dependents:   WattEye

Fork of NTPClient by Donatien Garnier

Committer:
WiredHome
Date:
Sat Jan 16 20:47:33 2016 +0000
Revision:
9:2f607bafc29e
Parent:
8:802277794640
NTPClient was computing the offset from the server to the client incorrectly (when it was promoting 32 to 64bit values. The result was time-jumps (e.g. from 2016 to 2084) and then back. This was most noticeable with the WiFly interface (extra delays)

Who changed what in which revision?

UserRevisionLine numberNew contents of line
donatien 0:04a82df0f587 1 /* NTPClient.cpp */
donatien 2:9a64a50df235 2 /* Copyright (C) 2012 mbed.org, MIT License
donatien 2:9a64a50df235 3 *
donatien 2:9a64a50df235 4 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
donatien 2:9a64a50df235 5 * and associated documentation files (the "Software"), to deal in the Software without restriction,
donatien 2:9a64a50df235 6 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
donatien 2:9a64a50df235 7 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
donatien 2:9a64a50df235 8 * furnished to do so, subject to the following conditions:
donatien 2:9a64a50df235 9 *
donatien 2:9a64a50df235 10 * The above copyright notice and this permission notice shall be included in all copies or
donatien 2:9a64a50df235 11 * substantial portions of the Software.
donatien 2:9a64a50df235 12 *
donatien 2:9a64a50df235 13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
donatien 2:9a64a50df235 14 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
donatien 2:9a64a50df235 15 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
donatien 2:9a64a50df235 16 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
donatien 2:9a64a50df235 17 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
donatien 2:9a64a50df235 18 */
WiredHome 9:2f607bafc29e 19 #include "mbed.h" //time() and set_time()
donatien 0:04a82df0f587 20
WiredHome 9:2f607bafc29e 21 //#define DEBUG "NTPc"
WiredHome 9:2f607bafc29e 22
WiredHome 9:2f607bafc29e 23 #if (defined(DEBUG))
donatien 2:9a64a50df235 24 #include <cstdio>
WiredHome 9:2f607bafc29e 25 #define INFO(x, ...) std::printf("[INF %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 9:2f607bafc29e 26 #define WARN(x, ...) std::printf("[WRN %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 9:2f607bafc29e 27 #define ERR(x, ...) std::printf("[ERR %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 9:2f607bafc29e 28 static void HexDump(const char * title, const uint8_t * p, int count)
WiredHome 9:2f607bafc29e 29 {
WiredHome 9:2f607bafc29e 30 int i;
WiredHome 9:2f607bafc29e 31 char buf[100] = "0000: ";
WiredHome 9:2f607bafc29e 32
WiredHome 9:2f607bafc29e 33 if (*title)
WiredHome 9:2f607bafc29e 34 INFO("%s", title);
WiredHome 9:2f607bafc29e 35 for (i=0; i<count; ) {
WiredHome 9:2f607bafc29e 36 sprintf(buf + strlen(buf), "%02X ", *(p+i));
WiredHome 9:2f607bafc29e 37 if ((++i & 0x0F) == 0x00) {
WiredHome 9:2f607bafc29e 38 INFO("%s", buf);
WiredHome 9:2f607bafc29e 39 if (i < count)
WiredHome 9:2f607bafc29e 40 sprintf(buf, "%04X: ", i);
WiredHome 9:2f607bafc29e 41 else
WiredHome 9:2f607bafc29e 42 buf[0] = '\0';
WiredHome 9:2f607bafc29e 43 }
WiredHome 9:2f607bafc29e 44 }
WiredHome 9:2f607bafc29e 45 if (strlen(buf))
WiredHome 9:2f607bafc29e 46 INFO("%s", buf);
WiredHome 9:2f607bafc29e 47 }
donatien 2:9a64a50df235 48 #else
donatien 2:9a64a50df235 49 //Disable debug
WiredHome 9:2f607bafc29e 50 #define INFO(x, ...)
donatien 2:9a64a50df235 51 #define WARN(x, ...)
WiredHome 8:802277794640 52 #define ERR(x, ...)
WiredHome 9:2f607bafc29e 53 #define HexDump(a,b,c)
donatien 0:04a82df0f587 54 #endif
donatien 0:04a82df0f587 55
donatien 0:04a82df0f587 56 #include "NTPClient.h"
donatien 0:04a82df0f587 57
donatien 1:b221a8765b3f 58 #include "UDPSocket.h"
donatien 1:b221a8765b3f 59
donatien 0:04a82df0f587 60
donatien 0:04a82df0f587 61 #define NTP_PORT 123
donatien 0:04a82df0f587 62 #define NTP_CLIENT_PORT 0 //Random port
donatien 0:04a82df0f587 63 #define NTP_TIMESTAMP_DELTA 2208988800ull //Diff btw a UNIX timestamp (Starting Jan, 1st 1970) and a NTP timestamp (Starting Jan, 1st 1900)
donatien 0:04a82df0f587 64
donatien 2:9a64a50df235 65 NTPClient::NTPClient() : m_sock()
donatien 0:04a82df0f587 66 {
donatien 0:04a82df0f587 67
donatien 0:04a82df0f587 68 }
donatien 0:04a82df0f587 69
donatien 2:9a64a50df235 70 NTPResult NTPClient::setTime(const char* host, uint16_t port, uint32_t timeout)
donatien 0:04a82df0f587 71 {
WiredHome 9:2f607bafc29e 72 #ifdef DEBUG
WiredHome 8:802277794640 73 time_t ctTime;
WiredHome 8:802277794640 74 ctTime = time(NULL);
WiredHome 9:2f607bafc29e 75 INFO("Time is currently (UTC): %s", ctime(&ctTime));
donatien 0:04a82df0f587 76 #endif
donatien 0:04a82df0f587 77
WiredHome 8:802277794640 78 //Create & bind socket
WiredHome 9:2f607bafc29e 79 INFO("Binding socket");
WiredHome 8:802277794640 80 m_sock.bind(0); //Bind to a random port
donatien 0:04a82df0f587 81
WiredHome 8:802277794640 82 m_sock.set_blocking(false, timeout); //Set not blocking
WiredHome 8:802277794640 83
WiredHome 8:802277794640 84 struct NTPPacket pkt;
donatien 0:04a82df0f587 85
WiredHome 8:802277794640 86 //Now ping the server and wait for response
WiredHome 9:2f607bafc29e 87 INFO("Ping");
WiredHome 8:802277794640 88 //Prepare NTP Packet:
WiredHome 8:802277794640 89 pkt.li = 0; //Leap Indicator : No warning
WiredHome 8:802277794640 90 pkt.vn = 4; //Version Number : 4
WiredHome 8:802277794640 91 pkt.mode = 3; //Client mode
WiredHome 8:802277794640 92 pkt.stratum = 0; //Not relevant here
WiredHome 8:802277794640 93 pkt.poll = 0; //Not significant as well
WiredHome 8:802277794640 94 pkt.precision = 0; //Neither this one is
donatien 0:04a82df0f587 95
WiredHome 8:802277794640 96 pkt.rootDelay = 0; //Or this one
WiredHome 8:802277794640 97 pkt.rootDispersion = 0; //Or that one
WiredHome 8:802277794640 98 pkt.refId = 0; //...
donatien 0:04a82df0f587 99
WiredHome 8:802277794640 100 pkt.refTm_s = 0;
WiredHome 8:802277794640 101 pkt.origTm_s = 0;
WiredHome 8:802277794640 102 pkt.rxTm_s = 0;
WiredHome 8:802277794640 103 pkt.txTm_s = htonl( NTP_TIMESTAMP_DELTA + time(NULL) ); //WARN: We are in LE format, network byte order is BE
WiredHome 9:2f607bafc29e 104 INFO("pkt.txTm_s = %u", ntohl(pkt.txTm_s) );
WiredHome 8:802277794640 105 pkt.refTm_f = pkt.origTm_f = pkt.rxTm_f = pkt.txTm_f = 0;
WiredHome 8:802277794640 106
WiredHome 9:2f607bafc29e 107 HexDump("NTP Post", (uint8_t *)&pkt, sizeof(NTPPacket));
WiredHome 9:2f607bafc29e 108
WiredHome 8:802277794640 109 Endpoint outEndpoint;
WiredHome 9:2f607bafc29e 110 INFO("outEndpoint instantiated");
WiredHome 9:2f607bafc29e 111
WiredHome 8:802277794640 112 if( outEndpoint.set_address(host, port) < 0) {
WiredHome 8:802277794640 113 m_sock.close();
WiredHome 8:802277794640 114 return NTP_DNS;
WiredHome 8:802277794640 115 }
WiredHome 9:2f607bafc29e 116 INFO("outEndpoint: %s:%d", outEndpoint.get_address(), outEndpoint.get_port());
WiredHome 8:802277794640 117
WiredHome 8:802277794640 118 //Set timeout, non-blocking and wait using select
WiredHome 8:802277794640 119 int ret = m_sock.sendTo( outEndpoint, (char*)&pkt, sizeof(NTPPacket) );
WiredHome 8:802277794640 120 if (ret < 0 ) {
WiredHome 8:802277794640 121 ERR("Could not send packet");
WiredHome 8:802277794640 122 m_sock.close();
WiredHome 8:802277794640 123 return NTP_CONN;
WiredHome 8:802277794640 124 }
donatien 0:04a82df0f587 125
WiredHome 8:802277794640 126 //Read response
WiredHome 8:802277794640 127 Endpoint inEndpoint;
WiredHome 9:2f607bafc29e 128 INFO(" inEndpoint instantiated: %s.", inEndpoint.get_address());
WiredHome 9:2f607bafc29e 129
WiredHome 8:802277794640 130 // Set the inEndpoint address property
WiredHome 8:802277794640 131 inEndpoint.set_address(outEndpoint.get_address(), 0);
donatien 2:9a64a50df235 132
WiredHome 9:2f607bafc29e 133 INFO(" inEndpoint: %s", inEndpoint.get_address());
WiredHome 9:2f607bafc29e 134
WiredHome 9:2f607bafc29e 135 INFO("Pong");
WiredHome 9:2f607bafc29e 136 int loopLimit = 20; // semi-randomly selected so it doesn't hang forever here...
WiredHome 8:802277794640 137 do {
WiredHome 9:2f607bafc29e 138 ret = m_sock.receiveFrom( inEndpoint, (char*)&pkt, sizeof(NTPPacket) );
WiredHome 8:802277794640 139 if(ret < 0) {
WiredHome 8:802277794640 140 ERR("Could not receive packet");
WiredHome 8:802277794640 141 m_sock.close();
WiredHome 8:802277794640 142 return NTP_CONN;
WiredHome 8:802277794640 143 }
WiredHome 9:2f607bafc29e 144 INFO(".");
WiredHome 9:2f607bafc29e 145 loopLimit--;
WiredHome 9:2f607bafc29e 146 } while( strcmp(outEndpoint.get_address(), inEndpoint.get_address()) != 0 && loopLimit > 0);
WiredHome 8:802277794640 147
WiredHome 8:802277794640 148 if(ret < (int)sizeof(NTPPacket)) { //TODO: Accept chunks
WiredHome 8:802277794640 149 ERR("Receive packet size does not match");
WiredHome 8:802277794640 150 m_sock.close();
WiredHome 8:802277794640 151 return NTP_PRTCL;
donatien 0:04a82df0f587 152 }
donatien 0:04a82df0f587 153
WiredHome 8:802277794640 154 if( pkt.stratum == 0) { //Kiss of death message : Not good !
WiredHome 8:802277794640 155 ERR("Kissed to death!");
WiredHome 8:802277794640 156 m_sock.close();
WiredHome 8:802277794640 157 return NTP_PRTCL;
WiredHome 8:802277794640 158 }
donatien 0:04a82df0f587 159
WiredHome 9:2f607bafc29e 160 HexDump("NTP Info", (uint8_t *)&pkt, sizeof(NTPPacket));
WiredHome 9:2f607bafc29e 161
WiredHome 8:802277794640 162 //Correct Endianness
WiredHome 8:802277794640 163 pkt.refTm_s = ntohl( pkt.refTm_s );
WiredHome 8:802277794640 164 pkt.refTm_f = ntohl( pkt.refTm_f );
WiredHome 8:802277794640 165 pkt.origTm_s = ntohl( pkt.origTm_s );
WiredHome 8:802277794640 166 pkt.origTm_f = ntohl( pkt.origTm_f );
WiredHome 8:802277794640 167 pkt.rxTm_s = ntohl( pkt.rxTm_s );
WiredHome 8:802277794640 168 pkt.rxTm_f = ntohl( pkt.rxTm_f );
WiredHome 8:802277794640 169 pkt.txTm_s = ntohl( pkt.txTm_s );
WiredHome 8:802277794640 170 pkt.txTm_f = ntohl( pkt.txTm_f );
donatien 0:04a82df0f587 171
WiredHome 8:802277794640 172 //Compute offset, see RFC 4330 p.13
WiredHome 8:802277794640 173 uint32_t destTm_s = (NTP_TIMESTAMP_DELTA + time(NULL));
WiredHome 9:2f607bafc29e 174 INFO("destTm_s = %u", destTm_s);
WiredHome 9:2f607bafc29e 175 INFO("pkt.txTm_s = %u", pkt.txTm_s );
WiredHome 9:2f607bafc29e 176
WiredHome 9:2f607bafc29e 177 // Modification by David Smart
WiredHome 9:2f607bafc29e 178 // The setTime function was computing the offset incorrectly as the value was promoted to 64-bit.
WiredHome 9:2f607bafc29e 179 // The side effect was that a negative offset ended up as a very large positive (e.g. jump from
WiredHome 9:2f607bafc29e 180 // 2016 to 2084). This change revises that computation.
WiredHome 9:2f607bafc29e 181 int64_t offset = ( ((int64_t)pkt.rxTm_s - pkt.origTm_s ) + ((int64_t) pkt.txTm_s - destTm_s ) ) / 2; //Avoid overflow
WiredHome 9:2f607bafc29e 182
WiredHome 9:2f607bafc29e 183 // delay is not needed, this was for diagnostic purposes only.
WiredHome 9:2f607bafc29e 184 //int64_t delay = ((int64_t) destTm_s - pkt.origTm_s) - ((int64_t) pkt.txTm_s - pkt.rxTm_s);
WiredHome 9:2f607bafc29e 185 INFO("txTm_s @%u", pkt.txTm_s);
WiredHome 9:2f607bafc29e 186 INFO("origTm_s @%u", pkt.origTm_s);
WiredHome 9:2f607bafc29e 187 INFO("rxTm_s @%u", pkt.rxTm_s);
WiredHome 9:2f607bafc29e 188 INFO("destTm_s @%u", destTm_s);
WiredHome 9:2f607bafc29e 189 INFO("Offset: %lld", offset);
WiredHome 9:2f607bafc29e 190 //INFO("Delay: %lld", delay);
WiredHome 9:2f607bafc29e 191
WiredHome 9:2f607bafc29e 192 INFO(" time: %u", time(NULL));
WiredHome 8:802277794640 193 //Set time accordingly
WiredHome 8:802277794640 194 set_time( time(NULL) + offset );
donatien 0:04a82df0f587 195
donatien 2:9a64a50df235 196 #ifdef __DEBUG__
WiredHome 8:802277794640 197 ctTime = time(NULL);
WiredHome 9:2f607bafc29e 198 INFO("Time is now (UTC): %s", ctime(&ctTime));
donatien 0:04a82df0f587 199 #endif
donatien 0:04a82df0f587 200
WiredHome 8:802277794640 201 m_sock.close();
donatien 0:04a82df0f587 202
WiredHome 8:802277794640 203 return NTP_OK;
donatien 0:04a82df0f587 204 }
donatien 0:04a82df0f587 205