Net stack with AutoIP enabled

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers NTPClient.cpp Source File

NTPClient.cpp

00001 
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 
00026 #include <stdio.h>
00027 
00028 //#define __DEBUG
00029 #include "dbg/dbg.h"
00030 
00031 #define NTP_PORT 123
00032 #define NTP_CLIENT_PORT 0//50420 //Random port
00033 #define NTP_REQUEST_TIMEOUT 15000
00034 #define NTP_TIMESTAMP_DELTA 2208988800ull //Diff btw a UNIX timestamp (Starting Jan, 1st 1970) and a NTP timestamp (Starting Jan, 1st 1900)
00035 
00036 #define htons( x ) ( (( x << 8 ) & 0xFF00) | (( x >> 8 ) & 0x00FF) )
00037 #define ntohs( x ) (htons(x))
00038 
00039 #define htonl( x ) ( (( x << 24 ) & 0xFF000000)  \
00040                    | (( x <<  8 ) & 0x00FF0000)  \
00041                    | (( x >>  8 ) & 0x0000FF00)  \
00042                    | (( x >> 24 ) & 0x000000FF)  )
00043 #define ntohl( x ) (htonl(x))
00044 
00045 NTPClient::NTPClient() : NetService(false), m_state(NTP_PING), m_pCbItem(NULL), m_pCbMeth(NULL), m_pCb(NULL),
00046 m_watchdog(), m_timeout(0), m_closed(true), m_host(), m_pDnsReq(NULL), m_blockingResult(NTP_PROCESSING)
00047 {
00048   setTimeout(NTP_REQUEST_TIMEOUT);
00049   DBG("\r\nNew NTPClient %p\r\n",this);
00050 }
00051 
00052 NTPClient::~NTPClient()
00053 {
00054   close();
00055 }
00056 
00057 //High level setup functions
00058 NTPResult NTPClient::setTime(const Host& host) //Blocking
00059 {
00060   doSetTime(host);
00061   return blockingProcess();
00062 }
00063 
00064 NTPResult NTPClient::setTime(const Host& host, void (*pMethod)(NTPResult)) //Non blocking
00065 {
00066   setOnResult(pMethod);
00067   doSetTime(host);
00068   return NTP_PROCESSING;
00069 }
00070 
00071 #if 0 //For doc only
00072 template<class T> 
00073 NTPResult NTPClient::setTime(const Host& host, T* pItem, void (T::*pMethod)(NTPResult)) //Non blocking
00074 {
00075   setOnResult(pItem, pMethod);
00076   doSetTime(host);
00077   return NTP_PROCESSING;
00078 }
00079 #endif
00080 
00081 void NTPClient::doSetTime(const Host& host)
00082 {
00083   init();
00084   resetTimeout();
00085   m_host = host;
00086   if(!m_host.getPort())
00087   {
00088     m_host.setPort(NTP_PORT);
00089   }
00090   if(m_host.getIp().isNull())
00091   {
00092     //DNS query required
00093     m_pDnsReq = new DNSRequest();
00094     DBG("\r\nNTPClient : DNSRequest %p\r\n", m_pDnsReq);
00095     m_pDnsReq->setOnReply(this, &NTPClient::onDNSReply);
00096     m_pDnsReq->resolve(&m_host);
00097     return;
00098   }
00099   open();
00100 }
00101 
00102 void NTPClient::setOnResult( void (*pMethod)(NTPResult) )
00103 {
00104   m_pCb = pMethod;
00105 }
00106 
00107 void NTPClient::close()
00108 {
00109   if(m_closed)
00110     return;
00111   m_closed = true; //Prevent recursive calling or calling on an object being destructed by someone else
00112   m_watchdog.stop();
00113   m_pUDPSocket->resetOnEvent();
00114   m_pUDPSocket->close();
00115   delete m_pUDPSocket;
00116   if( m_pDnsReq )
00117   {
00118     m_pDnsReq->close();
00119     delete m_pDnsReq;
00120     m_pDnsReq = NULL;
00121   }
00122 }
00123 
00124 void NTPClient::poll() //Called by NetServices
00125 {
00126   if( (!m_closed) && (m_watchdog.read_ms() >= m_timeout) )
00127   {
00128     onTimeout();
00129   }
00130 }
00131 
00132 void NTPClient::init() //Create and setup socket if needed
00133 {
00134   if(!m_closed) //Already opened
00135     return;
00136   m_state = NTP_PING;
00137   m_pUDPSocket = new UDPSocket;
00138   m_pUDPSocket->setOnEvent(this, &NTPClient::onUDPSocketEvent);
00139   m_closed = false;
00140   DBG("NTPClient: Init OK\n");
00141 }
00142 
00143 void NTPClient::open()
00144 {
00145   resetTimeout();
00146   DBG("Opening connection\n");
00147   m_state = NTP_PING;
00148   Host localhost(IpAddr(127,0,0,1), NTP_CLIENT_PORT, "localhost");
00149   m_pUDPSocket->bind(localhost);
00150   set_time( 1280000000 ); //End of July 2010... just there to limit offset range
00151   process();
00152 }
00153 
00154 #define MIN(a,b) ((a)<(b))?(a):(b)
00155 void NTPClient::process() //Main state-machine
00156 {
00157   int len;
00158   Host host;
00159   
00160   switch(m_state)
00161   {
00162   case NTP_PING:
00163     DBG("\r\nPing\r\n");
00164     //Prepare NTP Packet:
00165     m_pkt.li = 0; //Leap Indicator : No warning
00166     m_pkt.vn = 4; //Version Number : 4
00167     m_pkt.mode = 3; //Client mode
00168     m_pkt.stratum = 0; //Not relevant here
00169     m_pkt.poll = 0; //Not significant as well
00170     m_pkt.precision = 0; //Neither this one is
00171     
00172     m_pkt.rootDelay = 0; //Or this one
00173     m_pkt.rootDispersion = 0; //Or that one
00174     m_pkt.refId = 0; //...
00175     
00176     m_pkt.refTm_s = 0;
00177     m_pkt.origTm_s = 0;
00178     m_pkt.rxTm_s = 0;
00179     m_pkt.txTm_s = htonl( NTP_TIMESTAMP_DELTA + time(NULL) ); //WARN: We are in LE format, network byte order is BE
00180     
00181     m_pkt.refTm_f = m_pkt.origTm_f = m_pkt.rxTm_f = m_pkt.txTm_f = 0;
00182     
00183     //Hex Dump:
00184     DBG("\r\nDump Tx:\r\n");
00185     for(int i = 0; i< sizeof(NTPPacket); i++)
00186     {
00187       DBG("%02x ", *((char*)&m_pkt + i));
00188     }
00189     DBG("\r\n\r\n");
00190     
00191     len = m_pUDPSocket->sendto( (char*)&m_pkt, sizeof(NTPPacket), &m_host );
00192     if(len < sizeof(NTPPacket))
00193       { onResult(NTP_PRTCL); close(); return; }
00194       
00195     m_state = NTP_PONG; 
00196           
00197     break;
00198   
00199   case NTP_PONG:
00200     DBG("\r\nPong\r\n");
00201     while( len = m_pUDPSocket->recvfrom( (char*)&m_pkt, sizeof(NTPPacket), &host ) )
00202     {
00203       if( len <= 0 )
00204         break;
00205       if( !host.getIp().isEq(m_host.getIp()) )
00206         continue; //Not our packet
00207       if( len > 0 )
00208         break;
00209     }
00210     
00211     if(len == 0)
00212       return; //Wait for the next packet
00213     
00214     if(len < 0)
00215       { onResult(NTP_PRTCL); close(); return; }
00216     
00217     if(len < sizeof(NTPPacket)) //TODO: Accept chunks
00218       { onResult(NTP_PRTCL); close(); return; }
00219       
00220     //Hex Dump:
00221     DBG("\r\nDump Rx:\r\n");
00222     for(int i = 0; i< sizeof(NTPPacket); i++)
00223     {
00224       DBG("%02x ", *((char*)&m_pkt + i));
00225     }
00226     DBG("\r\n\r\n");
00227       
00228     if( m_pkt.stratum == 0)  //Kiss of death message : Not good !
00229     {
00230       onResult(NTP_PRTCL); close(); return;
00231     }
00232     
00233     //Correct Endianness
00234     m_pkt.refTm_s = ntohl( m_pkt.refTm_s );
00235     m_pkt.refTm_f = ntohl( m_pkt.refTm_f );
00236     m_pkt.origTm_s = ntohl( m_pkt.origTm_s );
00237     m_pkt.origTm_f = ntohl( m_pkt.origTm_f );
00238     m_pkt.rxTm_s = ntohl( m_pkt.rxTm_s );
00239     m_pkt.rxTm_f = ntohl( m_pkt.rxTm_f );
00240     m_pkt.txTm_s = ntohl( m_pkt.txTm_s );
00241     m_pkt.txTm_f = ntohl( m_pkt.txTm_f );
00242     
00243     //Compute offset, see RFC 4330 p.13
00244     uint32_t destTm_s = (NTP_TIMESTAMP_DELTA + time(NULL));
00245     //int32_t origTm = (int32_t) ((uint64_t) m_pkt.origTm - NTP_TIMESTAMP_DELTA); //Convert in local 32 bits timestamps
00246     //int32_t rxTm = (int32_t) ((uint64_t) m_pkt.rxTm - NTP_TIMESTAMP_DELTA); //Convert in local 32 bits timestamps
00247     //int32_t txTm = (int32_t) ((uint64_t) m_pkt.txTm - NTP_TIMESTAMP_DELTA); //Convert in local 32 bits timestamps
00248    // int64_t offset = ( ( ( m_pkt.rxTm_s - m_pkt.origTm_s ) + ( m_pkt.txTm_s - destTm_s ) ) << 32 + ( ( m_pkt.rxTm_f - m_pkt.origTm_f ) + ( m_pkt.txTm_f - 0 ) ) ) / 2;
00249     int64_t offset = ( (int64_t)( m_pkt.rxTm_s - m_pkt.origTm_s ) + (int64_t) ( m_pkt.txTm_s - destTm_s ) ) / 2; //Avoid overflow
00250     DBG("\r\nSent @%d\r\n", m_pkt.txTm_s);
00251     DBG("\r\nOffset: %d\r\n", offset);
00252     //Set time accordingly
00253     set_time( time(NULL) + (offset /*>> 32*/) );
00254     
00255     onResult(NTP_OK);
00256     close();
00257       
00258     break;
00259   }
00260 }
00261 
00262 void NTPClient::setTimeout(int ms)
00263 {
00264   m_timeout = ms;
00265 }
00266 
00267 void NTPClient::resetTimeout()
00268 {
00269   m_watchdog.reset();
00270   m_watchdog.start();
00271 }
00272 
00273 void NTPClient::onTimeout() //Connection has timed out
00274 {
00275   close();
00276   onResult(NTP_TIMEOUT);
00277 }
00278 
00279 void NTPClient::onDNSReply(DNSReply r)
00280 {
00281   if(m_closed)
00282   {
00283     DBG("\r\nWARN: Discarded\r\n");
00284     return;
00285   }
00286   
00287   if( r != DNS_FOUND )
00288   {
00289     DBG("\r\nCould not resolve hostname.\r\n");
00290     onResult(NTP_DNS);
00291     close();
00292     return;
00293   }
00294   DBG("\r\nDNS resolved.\r\n");
00295   m_pDnsReq->close();
00296   delete m_pDnsReq;
00297   m_pDnsReq=NULL;
00298   open();
00299 }
00300   
00301 void NTPClient::onUDPSocketEvent(UDPSocketEvent e)
00302 {
00303   resetTimeout();
00304   switch(e)
00305   {
00306   case UDPSOCKET_READABLE: //The only event for now
00307     resetTimeout();
00308     process();
00309     break;
00310   }
00311 }
00312 
00313 void NTPClient::onResult(NTPResult r) //Must be called by impl when the request completes
00314 {
00315   if(m_pCbItem && m_pCbMeth)
00316     (m_pCbItem->*m_pCbMeth)(r);
00317   else if(m_pCb)
00318     m_pCb(r);
00319   m_blockingResult = r; //Blocking mode
00320 }
00321 
00322 NTPResult NTPClient::blockingProcess() //Called in blocking mode, calls Net::poll() until return code is available
00323 {
00324   m_blockingResult = NTP_PROCESSING;
00325   do
00326   {
00327     Net::poll();
00328   } while(m_blockingResult == NTP_PROCESSING);
00329   Net::poll(); //Necessary for cleanup
00330   return m_blockingResult;
00331 }