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

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers NTPClient.cpp Source File

NTPClient.cpp

00001 /* NTPClient.cpp */
00002 /* Copyright (C) 2012 mbed.org, MIT License
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
00005  * and associated documentation files (the "Software"), to deal in the Software without restriction,
00006  * including without limitation the rights to use, copy, modify, merge, publish, distribute,
00007  * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
00008  * furnished to do so, subject to the following conditions:
00009  *
00010  * The above copyright notice and this permission notice shall be included in all copies or
00011  * substantial portions of the Software.
00012  *
00013  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
00014  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00015  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
00016  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00017  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00018  */
00019 #include "mbed.h" //time() and set_time()
00020 
00021 //#define DEBUG "NTPc"
00022 
00023 #if (defined(DEBUG))
00024 #include <cstdio>
00025 #define INFO(x, ...) std::printf("[INF %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
00026 #define WARN(x, ...) std::printf("[WRN %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
00027 #define ERR(x, ...)  std::printf("[ERR %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
00028 static void HexDump(const char * title, const uint8_t * p, int count)
00029 {
00030     int i;
00031     char buf[100] = "0000: ";
00032 
00033     if (*title)
00034         INFO("%s", title);
00035     for (i=0; i<count; ) {
00036         sprintf(buf + strlen(buf), "%02X ", *(p+i));
00037         if ((++i & 0x0F) == 0x00) {
00038             INFO("%s", buf);
00039             if (i < count)
00040                 sprintf(buf, "%04X: ", i);
00041             else
00042                 buf[0] = '\0';
00043         }
00044     }
00045     if (strlen(buf))
00046         INFO("%s", buf);
00047 }
00048 #else
00049 //Disable debug
00050 #define INFO(x, ...)
00051 #define WARN(x, ...)
00052 #define ERR(x, ...)
00053 #define HexDump(a,b,c)
00054 #endif
00055 
00056 #include "NTPClient.h"
00057 
00058 #include "UDPSocket.h"
00059 
00060 
00061 #define NTP_PORT 123
00062 #define NTP_CLIENT_PORT 0 //Random port
00063 #define NTP_TIMESTAMP_DELTA 2208988800ull //Diff btw a UNIX timestamp (Starting Jan, 1st 1970) and a NTP timestamp (Starting Jan, 1st 1900)
00064 
00065 NTPClient::NTPClient() : m_sock()
00066 {
00067 
00068 }
00069 
00070 NTPResult NTPClient::setTime(const char* host, uint16_t port, uint32_t timeout)
00071 {
00072 #ifdef DEBUG
00073     time_t ctTime;
00074     ctTime = time(NULL);
00075     INFO("Time is currently (UTC): %s", ctime(&ctTime));
00076 #endif
00077 
00078     //Create & bind socket
00079     INFO("Binding socket");
00080     m_sock.bind(0); //Bind to a random port
00081 
00082     m_sock.set_blocking(false, timeout); //Set not blocking
00083 
00084     struct NTPPacket pkt;
00085 
00086     //Now ping the server and wait for response
00087     INFO("Ping");
00088     //Prepare NTP Packet:
00089     pkt.li = 0; //Leap Indicator : No warning
00090     pkt.vn = 4; //Version Number : 4
00091     pkt.mode = 3; //Client mode
00092     pkt.stratum = 0; //Not relevant here
00093     pkt.poll = 0; //Not significant as well
00094     pkt.precision = 0; //Neither this one is
00095 
00096     pkt.rootDelay = 0; //Or this one
00097     pkt.rootDispersion = 0; //Or that one
00098     pkt.refId = 0; //...
00099 
00100     pkt.refTm_s = 0;
00101     pkt.origTm_s = 0;
00102     pkt.rxTm_s = 0;
00103     pkt.txTm_s = htonl( NTP_TIMESTAMP_DELTA + time(NULL) ); //WARN: We are in LE format, network byte order is BE
00104     INFO("pkt.txTm_s = %u", ntohl(pkt.txTm_s) );
00105     pkt.refTm_f = pkt.origTm_f = pkt.rxTm_f = pkt.txTm_f = 0;
00106 
00107     HexDump("NTP Post", (uint8_t *)&pkt, sizeof(NTPPacket));
00108     
00109     Endpoint outEndpoint;
00110     INFO("outEndpoint instantiated");
00111     
00112     if( outEndpoint.set_address(host, port) < 0) {
00113         m_sock.close();
00114         return NTP_DNS;
00115     }
00116     INFO("outEndpoint: %s:%d", outEndpoint.get_address(), outEndpoint.get_port());
00117 
00118     //Set timeout, non-blocking and wait using select
00119     int ret = m_sock.sendTo( outEndpoint, (char*)&pkt, sizeof(NTPPacket) );
00120     if (ret < 0 ) {
00121         ERR("Could not send packet");
00122         m_sock.close();
00123         return NTP_CONN;
00124     }
00125 
00126     //Read response
00127     Endpoint inEndpoint;
00128     INFO(" inEndpoint instantiated: %s.", inEndpoint.get_address());
00129     
00130     // Set the inEndpoint address property
00131     inEndpoint.set_address(outEndpoint.get_address(), 0);
00132 
00133     INFO(" inEndpoint: %s", inEndpoint.get_address());
00134 
00135     INFO("Pong");
00136     int loopLimit = 20;  // semi-randomly selected so it doesn't hang forever here...
00137     do {
00138         ret = m_sock.receiveFrom( inEndpoint, (char*)&pkt, sizeof(NTPPacket) );
00139         if(ret < 0) {
00140             ERR("Could not receive packet");
00141             m_sock.close();
00142             return NTP_CONN;
00143         }
00144         INFO(".");
00145         loopLimit--;
00146     } while( strcmp(outEndpoint.get_address(), inEndpoint.get_address()) != 0 && loopLimit > 0);
00147 
00148     if(ret < (int)sizeof(NTPPacket)) { //TODO: Accept chunks
00149         ERR("Receive packet size does not match");
00150         m_sock.close();
00151         return NTP_PRTCL;
00152     }
00153 
00154     if( pkt.stratum == 0) { //Kiss of death message : Not good !
00155         ERR("Kissed to death!");
00156         m_sock.close();
00157         return NTP_PRTCL;
00158     }
00159 
00160     HexDump("NTP Info", (uint8_t *)&pkt, sizeof(NTPPacket));
00161 
00162     //Correct Endianness
00163     pkt.refTm_s = ntohl( pkt.refTm_s );
00164     pkt.refTm_f = ntohl( pkt.refTm_f );
00165     pkt.origTm_s = ntohl( pkt.origTm_s );
00166     pkt.origTm_f = ntohl( pkt.origTm_f );
00167     pkt.rxTm_s = ntohl( pkt.rxTm_s );
00168     pkt.rxTm_f = ntohl( pkt.rxTm_f );
00169     pkt.txTm_s = ntohl( pkt.txTm_s );
00170     pkt.txTm_f = ntohl( pkt.txTm_f );
00171 
00172     //Compute offset, see RFC 4330 p.13
00173     uint32_t destTm_s = (NTP_TIMESTAMP_DELTA + time(NULL));
00174     INFO("destTm_s = %u", destTm_s);
00175     INFO("pkt.txTm_s = %u", pkt.txTm_s );
00176     
00177     // Modification by David Smart
00178     // The setTime function was computing the offset incorrectly as the value was promoted to 64-bit.
00179     // The side effect was that a negative offset ended up as a very large positive (e.g. jump from 
00180     // 2016 to 2084). This change revises that computation.
00181     int64_t offset = ( ((int64_t)pkt.rxTm_s - pkt.origTm_s ) + ((int64_t) pkt.txTm_s - destTm_s ) ) / 2; //Avoid overflow
00182     
00183     // delay is not needed, this was for diagnostic purposes only.
00184     //int64_t delay = ((int64_t) destTm_s - pkt.origTm_s) - ((int64_t) pkt.txTm_s - pkt.rxTm_s);
00185     INFO("txTm_s   @%u", pkt.txTm_s);
00186     INFO("origTm_s @%u", pkt.origTm_s);
00187     INFO("rxTm_s   @%u", pkt.rxTm_s);
00188     INFO("destTm_s @%u", destTm_s);
00189     INFO("Offset: %lld", offset);
00190     //INFO("Delay:  %lld", delay);
00191     
00192     INFO(" time:    %u", time(NULL));
00193     //Set time accordingly
00194     set_time( time(NULL) + offset );
00195 
00196 #ifdef __DEBUG__
00197     ctTime = time(NULL);
00198     INFO("Time is now (UTC): %s", ctime(&ctTime));
00199 #endif
00200 
00201     m_sock.close();
00202 
00203     return NTP_OK;
00204 }
00205