The extracted NTP client from Segundos NetServices library, for use with the [[http://mbed.org/users/hlipka/libraries/NetServicesMin|NetServicesMin]] library. The only fixed bug is the memory leak / OOM problem. Needs the [[http://mbed.org/users/hlipka/libraries/DNSResolver|DNSResolver]] library as well.

Dependents:   SPIVFDclock LPC1768_AppBoard_Internet_LCD_Clock

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers NTPClient.cpp Source File

NTPClient.cpp

00001 #pragma diag_remark 1293
00002 /*
00003 Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
00004  
00005 Permission is hereby granted, free of charge, to any person obtaining a copy
00006 of this software and associated documentation files (the "Software"), to deal
00007 in the Software without restriction, including without limitation the rights
00008 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00009 copies of the Software, and to permit persons to whom the Software is
00010 furnished to do so, subject to the following conditions:
00011  
00012 The above copyright notice and this permission notice shall be included in
00013 all copies or substantial portions of the Software.
00014  
00015 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00016 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00017 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00018 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00019 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00020 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00021 THE SOFTWARE.
00022 */
00023 
00024 #include "NTPClient.h"
00025 #include "dnsresolve.h"
00026 
00027 #include <stdio.h>
00028 
00029 //#define __DEBUG
00030 #include "dbg/dbg.h"
00031 
00032 #define NTP_PORT 123
00033 #define NTP_CLIENT_PORT 0//50420 //Random port
00034 #define NTP_REQUEST_TIMEOUT 5000
00035 #define NTP_TIMESTAMP_DELTA 2208988800ull //Diff btw a UNIX timestamp (Starting Jan, 1st 1970) and a NTP timestamp (Starting Jan, 1st 1900)
00036 
00037 #define htons( x ) ( (( x << 8 ) & 0xFF00) | (( x >> 8 ) & 0x00FF) )
00038 #define ntohs( x ) (htons(x))
00039 
00040 #define htonl( x ) ( (( x << 24 ) & 0xFF000000)  \
00041                    | (( x <<  8 ) & 0x00FF0000)  \
00042                    | (( x >>  8 ) & 0x0000FF00)  \
00043                    | (( x >> 24 ) & 0x000000FF)  )
00044 #define ntohl( x ) (htonl(x))
00045 
00046 NTPClient::NTPClient() : 
00047     _state(NTP_PING), 
00048     _timeout(NTP_REQUEST_TIMEOUT), 
00049     _closed(true), 
00050     _host(), 
00051     _blockingResult(NTP_PROCESSING)
00052 {
00053   _watchdog=new Timer();
00054   DBG("\r\nNew NTPClient %p\r\n",this);
00055 }
00056 
00057 NTPClient::~NTPClient()
00058 {
00059   close();
00060   delete _watchdog;
00061 }
00062 
00063 //High level setup functions
00064 NTPResult NTPClient::setTime(const Host& host) //Blocking
00065 {
00066   doSetTime(host);
00067   return blockingProcess();
00068 }
00069 
00070 void NTPClient::doSetTime(const Host& host)
00071 {
00072   init();
00073   resetTimeout();
00074   _host = host;
00075   if(!_host.getPort())
00076   {
00077     _host.setPort(NTP_PORT);
00078   }
00079   if(_host.getIp().isNull())
00080   {
00081   
00082     DNSResolver *dr=new DNSResolver();
00083     IpAddr ad=dr->resolveName(host.getName());
00084     delete dr;
00085     if (ad.isNull())
00086     {
00087         onResult(NTP_DNS);
00088         return;
00089     }
00090     _host.setIp(ad);
00091   }
00092   open();
00093 }
00094 
00095 void NTPClient::close()
00096 {
00097   if(_closed)
00098     return;
00099   _closed = true; //Prevent recursive calling or calling on an object being destructed by someone else
00100   _watchdog->stop();
00101   _pUDPSocket->resetOnEvent();
00102   _pUDPSocket->close();
00103   delete _pUDPSocket;
00104 }
00105 
00106 void NTPClient::init() //Create and setup socket if needed
00107 {
00108   if(!_closed) //Already opened
00109     return;
00110   _state = NTP_PING;
00111   _pUDPSocket = new UDPSocket;
00112   _pUDPSocket->setOnEvent(this, &NTPClient::onUDPSocketEvent);
00113   _closed = false;
00114   DBG("NTPClient: Init OK\n");
00115 }
00116 
00117 void NTPClient::open()
00118 {
00119   resetTimeout();
00120   DBG("Opening connection\n");
00121   _state = NTP_PING;
00122   Host localhost(IpAddr(), NTP_CLIENT_PORT, "localhost"); //Any local address
00123   _pUDPSocket->bind(localhost);
00124   if ((int)time(NULL) < 1280000000) set_time( 1280000000 ); //End of July 2010... just there to limit offset range
00125 
00126   process();
00127 
00128 }
00129 
00130 #define MIN(a,b) ((a)<(b))?(a):(b)
00131 void NTPClient::process() //Main state-machine
00132 {
00133   int len;
00134   Host host;
00135   switch(_state)
00136   {
00137   case NTP_PING:
00138     DBG("Ping\r\n");
00139     //Prepare NTP Packet:
00140     _pkt.li = 0; //Leap Indicator : No warning
00141     _pkt.vn = 4; //Version Number : 4
00142     _pkt.mode = 3; //Client mode
00143     _pkt.stratum = 0; //Not relevant here
00144     _pkt.poll = 0; //Not significant as well
00145     _pkt.precision = 0; //Neither this one is
00146     
00147     _pkt.rootDelay = 0; //Or this one
00148     _pkt.rootDispersion = 0; //Or that one
00149     _pkt.refId = 0; //...
00150     
00151     _pkt.refTm_s = 0;
00152     _pkt.origTm_s = 0;
00153     _pkt.rxTm_s = 0;
00154     _pkt.txTm_s = htonl( NTP_TIMESTAMP_DELTA + time(NULL) ); //WARN: We are in LE format, network byte order is BE
00155     
00156     _pkt.refTm_f = _pkt.origTm_f = _pkt.rxTm_f = _pkt.txTm_f = 0;
00157     
00158     #ifdef __DEBUG
00159     //Hex Dump:
00160     DBG("Dump Tx:\r\n");
00161     for(int i = 0; i< sizeof(NTPPacket); i++)
00162     {
00163       DBGL("%02x ", *((char*)&_pkt + i));
00164     }
00165     DBGL("\r\n");
00166     #endif
00167     
00168     len = _pUDPSocket->sendto( (char*)&_pkt, sizeof(NTPPacket), &_host );
00169     if(len < sizeof(NTPPacket))
00170       { onResult(NTP_PRTCL); close(); return; }
00171       
00172     _state = NTP_PONG; 
00173           
00174     break;
00175   
00176   case NTP_PONG:
00177     DBG("Pong\r\n");
00178     while( len = _pUDPSocket->recvfrom( (char*)&_pkt, sizeof(NTPPacket), &host ) )
00179     {
00180       if( len <= 0 )
00181         break;
00182       if( !host.getIp().isEq(_host.getIp()) )
00183         continue; //Not our packet
00184       if( len > 0 )
00185         break;
00186     }
00187     
00188     if(len == 0)
00189       return; //Wait for the next packet
00190     
00191     if(len < 0)
00192       { onResult(NTP_PRTCL); close(); return; }
00193     
00194     if(len < sizeof(NTPPacket)) //TODO: Accept chunks
00195       { onResult(NTP_PRTCL); close(); return; }
00196       
00197     #ifdef __DEBUG
00198     //Hex Dump:
00199     DBG("Dump Rx:\r\n");
00200     for(int i = 0; i< sizeof(NTPPacket); i++)
00201     {
00202       DBGL("%02x ", *((char*)&_pkt + i));
00203     }
00204     DBGL("\r\n");
00205     #endif
00206       
00207     if( _pkt.stratum == 0)  //Kiss of death message : Not good !
00208     {
00209       onResult(NTP_PRTCL); close(); return;
00210     }
00211     
00212     //Correct Endianness
00213     _pkt.refTm_s = ntohl( _pkt.refTm_s );
00214     _pkt.refTm_f = ntohl( _pkt.refTm_f );
00215     _pkt.origTm_s = ntohl( _pkt.origTm_s );
00216     _pkt.origTm_f = ntohl( _pkt.origTm_f );
00217     _pkt.rxTm_s = ntohl( _pkt.rxTm_s );
00218     _pkt.rxTm_f = ntohl( _pkt.rxTm_f );
00219     _pkt.txTm_s = ntohl( _pkt.txTm_s );
00220     _pkt.txTm_f = ntohl( _pkt.txTm_f );
00221     
00222     //Compute offset, see RFC 4330 p.13
00223     uint32_t destTm_s = (NTP_TIMESTAMP_DELTA + time(NULL));
00224     //int32_t origTm = (int32_t) ((uint64_t) _pkt.origTm - NTP_TIMESTAMP_DELTA); //Convert in local 32 bits timestamps
00225     //int32_t rxTm = (int32_t) ((uint64_t) _pkt.rxTm - NTP_TIMESTAMP_DELTA); //Convert in local 32 bits timestamps
00226     //int32_t txTm = (int32_t) ((uint64_t) _pkt.txTm - NTP_TIMESTAMP_DELTA); //Convert in local 32 bits timestamps
00227    // int64_t offset = ( ( ( _pkt.rxT_s - m_pkt.origTm_s ) + ( m_pkt.txT_s - destTm_s ) ) << 32 + ( ( m_pkt.rxTm_f - m_pkt.origTm_f ) + ( m_pkt.txT_f - 0 ) ) ) / 2;
00228     int64_t offset = ( (int64_t)( _pkt.rxTm_s - _pkt.origTm_s ) + (int64_t) ( _pkt.txTm_s - destTm_s ) ) / 2; //Avoid overflow
00229     DBG("Sent @%d\r\n", _pkt.txTm_s);
00230     DBG("Offset: %d\r\n", offset);
00231 
00232     //Set time accordingly
00233     set_time( time(NULL) + (offset /*>> 32*/) );
00234     
00235     onResult(NTP_OK);
00236     close();
00237     break;
00238   }
00239 }
00240 
00241 void NTPClient::setTimeout(int ms)
00242 {
00243   _timeout = ms;
00244 }
00245 
00246 void NTPClient::resetTimeout()
00247 {
00248   _watchdog->reset();
00249   _watchdog->start();
00250 }
00251 
00252 void NTPClient::onTimeout() //Connection has timed out
00253 {
00254   close();
00255   onResult(NTP_TIMEOUT);
00256 }
00257 
00258 void NTPClient::onUDPSocketEvent(UDPSocketEvent e)
00259 {
00260   resetTimeout();
00261   switch(e)
00262   {
00263   case UDPSOCKET_READABLE: //The only event for now
00264     process();
00265     break;
00266   }
00267 }
00268 
00269 void NTPClient::onResult(NTPResult r) //Must be called by impl when the request completes
00270 {
00271   _blockingResult = r; //Blocking mode
00272 }
00273 
00274 NTPResult NTPClient::blockingProcess() //Called in blocking mode, calls Net::poll() until return code is available
00275 {
00276   _blockingResult = NTP_PROCESSING;
00277   do
00278   {
00279     Net::poll();
00280     wait_us(100);
00281     if (_watchdog->read_ms()>_timeout)
00282         return NTP_TIMEOUT;
00283   } while(_blockingResult == NTP_PROCESSING);
00284  
00285   Net::poll(); //Necessary for cleanup
00286 
00287   return _blockingResult;
00288 }