Ilya I / Mbed 2 deprecated iva2k_NetHttpServerTcpSockets

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 50420
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() : 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("New 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 #ifdef __LINKER_BUG_SOLVED__
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   m_host = host;
00085   if(m_host.getIp().isNull())
00086   {
00087     //DNS query required
00088     m_pDnsReq = new DNSRequest();
00089     DBG("NTPClient : DNSRequest %p\r\n", m_pDnsReq);
00090     m_pDnsReq->setOnReply(this, &NTPClient::onDNSReply);
00091     m_pDnsReq->resolve(&m_host);
00092     return;
00093   }
00094   if(!m_host.getPort())
00095   {
00096     m_host.setPort(NTP_PORT);
00097   }
00098   
00099   m_state = NTP_PING;
00100   Host localhost(IpAddr(127,0,0,1), NTP_CLIENT_PORT, "localhost");
00101   m_pUDPSocket->bind(localhost);
00102   set_time( 1280000000 ); //End of July 2010... just there to limit offset range
00103   process();
00104 }
00105 
00106 void NTPClient::setOnResult( void (*pMethod)(NTPResult) )
00107 {
00108   m_pCb = pMethod;
00109 }
00110 
00111 void NTPClient::init() //Create and setup socket if needed
00112 {
00113   if(!m_closed) //Already opened
00114     return;
00115   m_state = NTP_PING;
00116   m_pUDPSocket = new UDPSocket;
00117   m_pUDPSocket->setOnEvent(this, &NTPClient::onUDPSocketEvent);
00118   m_closed = false;
00119 }
00120 
00121 void NTPClient::close()
00122 {
00123   if(m_closed)
00124     return;
00125   m_closed = true; //Prevent recursive calling or calling on an object being destructed by someone else
00126   m_watchdog.detach();
00127   m_pUDPSocket->resetOnEvent();
00128   m_pUDPSocket->close();
00129   delete m_pUDPSocket;
00130   if( m_pDnsReq )
00131   {
00132     m_pDnsReq->close();
00133     delete m_pDnsReq;
00134     m_pDnsReq = NULL;
00135   }
00136 }
00137 
00138 #define MIN(a,b) ((a)<(b))?(a):(b)
00139 void NTPClient::process() //Main state-machine
00140 {
00141   NTPPacket pkt;
00142   int len;
00143   Host host;
00144 
00145   switch(m_state)
00146   {
00147   case NTP_PING:
00148     DBG("Ping\r\n");
00149     //Prepare NTP Packet:
00150     pkt.li = 0; //Leap Indicator : No warning
00151     pkt.vn = 4; //Version Number : 4
00152     pkt.mode = 3; //Client mode
00153     pkt.stratum = 0; //Not relevant here
00154     pkt.poll = 0; //Not significant as well
00155     pkt.precision = 0; //Neither this one is
00156     
00157     pkt.rootDelay = 0; //Or this one
00158     pkt.rootDispersion = 0; //Or that one
00159     pkt.refId = 0; //...
00160     
00161     pkt.refTm_s = 0;
00162     pkt.origTm_s = 0;
00163     pkt.rxTm_s = 0;
00164     pkt.txTm_s = htonl( NTP_TIMESTAMP_DELTA + time(NULL) ); //WARN: We are in LE format, network byte order is BE
00165     
00166     pkt.refTm_f = pkt.origTm_f = pkt.rxTm_f = pkt.txTm_f = 0;
00167     
00168     //Hex Dump:
00169     DBG("Dump Tx:\r\n");
00170     for(int i = 0; i< sizeof(NTPPacket); i++)
00171     {
00172       DBG("%02x ", *((char*)&pkt + i));
00173     }
00174     DBG("");
00175     
00176     len = m_pUDPSocket->sendto( (char*)&pkt, sizeof(NTPPacket), &m_host );
00177     if(len < sizeof(NTPPacket))
00178       { onResult(NTP_PRTCL); close(); return; }
00179       
00180     m_state = NTP_PONG; 
00181           
00182     break;
00183   
00184   case NTP_PONG:
00185     DBG("Pong\r\n");
00186     while( len = m_pUDPSocket->recvfrom( (char*)&pkt, sizeof(NTPPacket), &host ) )
00187     {
00188       if( len <= 0 )
00189         break;
00190       if( !host.getIp().isEq(m_host.getIp()) )
00191       //if( !ip_addr_cmp( &host.getIp().getStruct(), &m_host.getIp().getStruct() ) ) //Was working like that, trying nicer impl above
00192         continue; //Not our packet
00193       if( len > 0 )
00194         break;
00195     }
00196     
00197     if(len == 0)
00198       return; //Wait for the next packet
00199     
00200     if(len < 0)
00201       { onResult(NTP_PRTCL); close(); return; }
00202     
00203     if(len < sizeof(NTPPacket)) //TODO: Accept chunks
00204       { onResult(NTP_PRTCL); close(); return; }
00205       
00206     //Hex Dump:
00207     DBG("Dump Rx:\r\n");
00208     for(int i = 0; i< sizeof(NTPPacket); i++)
00209     {
00210       DBG("%02x ", *((char*)&pkt + i));
00211     }
00212     DBG("");
00213       
00214     if( pkt.stratum == 0)  //Kiss of death message : Not good !
00215     {
00216       onResult(NTP_PRTCL); close(); return;
00217     }
00218     
00219     //Correct Endianness
00220     pkt.refTm_s = ntohl( pkt.refTm_s );
00221     pkt.refTm_f = ntohl( pkt.refTm_f );
00222     pkt.origTm_s = ntohl( pkt.origTm_s );
00223     pkt.origTm_f = ntohl( pkt.origTm_f );
00224     pkt.rxTm_s = ntohl( pkt.rxTm_s );
00225     pkt.rxTm_f = ntohl( pkt.rxTm_f );
00226     pkt.txTm_s = ntohl( pkt.txTm_s );
00227     pkt.txTm_f = ntohl( pkt.txTm_f );
00228     
00229     //Compute offset, see RFC 4330 p.13
00230     uint32_t destTm_s = (NTP_TIMESTAMP_DELTA + time(NULL));
00231     //int32_t origTm = (int32_t) ((uint64_t) pkt.origTm - NTP_TIMESTAMP_DELTA); //Convert in local 32 bits timestamps
00232     //int32_t rxTm = (int32_t) ((uint64_t) pkt.rxTm - NTP_TIMESTAMP_DELTA); //Convert in local 32 bits timestamps
00233     //int32_t txTm = (int32_t) ((uint64_t) pkt.txTm - NTP_TIMESTAMP_DELTA); //Convert in local 32 bits timestamps
00234    // int64_t offset = ( ( ( pkt.rxTm_s - pkt.origTm_s ) + ( pkt.txTm_s - destTm_s ) ) << 32 + ( ( pkt.rxTm_f - pkt.origTm_f ) + ( pkt.txTm_f - 0 ) ) ) / 2;
00235     int64_t offset = ( (int64_t)( pkt.rxTm_s - pkt.origTm_s ) + (int64_t) ( pkt.txTm_s - destTm_s ) ) / 2; //Avoid overflow
00236     DBG("Sent @%d\r\n", pkt.txTm_s);
00237     DBG("Offset: %d\r\n", offset);
00238     //Set time accordingly
00239     set_time( time(NULL) + (offset /*>> 32*/) );
00240     
00241     onResult(NTP_OK);
00242     close();
00243       
00244     break;
00245   }
00246 }
00247 
00248 void NTPClient::setTimeout(int ms)
00249 {
00250   m_timeout = 1000*ms;
00251   resetTimeout();
00252 }
00253 
00254 void NTPClient::resetTimeout()
00255 {
00256   m_watchdog.detach();
00257   m_watchdog.attach_us<NTPClient>(this, &NTPClient::onTimeout, m_timeout);
00258 }
00259 
00260 void NTPClient::onTimeout() //Connection has timed out
00261 {
00262   close();
00263   onResult(NTP_TIMEOUT);
00264 }
00265 
00266 void NTPClient::onDNSReply(DNSReply r)
00267 {
00268   if(m_closed)
00269   {
00270     DBG("WARN: Discarded\r\n");
00271     return;
00272   }
00273   
00274   if( r != DNS_FOUND )
00275   {
00276     DBG("Could not resolve hostname.\r\n");
00277     onResult(NTP_DNS);
00278     close();
00279     return;
00280   }
00281   m_pDnsReq->close();
00282   delete m_pDnsReq;
00283   m_pDnsReq=NULL;
00284   doSetTime(m_host);
00285 }
00286   
00287 void NTPClient::onUDPSocketEvent(UDPSocketEvent e)
00288 {
00289   switch(e)
00290   {
00291   case UDPSOCKET_READABLE: //The only event for now
00292     resetTimeout();
00293     process();
00294     break;
00295   }
00296 }
00297 
00298 void NTPClient::onResult(NTPResult r) //Must be called by impl when the request completes
00299 {
00300   if(m_pCbItem && m_pCbMeth)
00301     (m_pCbItem->*m_pCbMeth)(r);
00302   else if(m_pCb)
00303     m_pCb(r);
00304   m_blockingResult = r; //Blocking mode
00305 }
00306 
00307 NTPResult NTPClient::blockingProcess() //Called in blocking mode, calls Net::poll() until return code is available
00308 {
00309   m_blockingResult = NTP_PROCESSING;
00310   do
00311   {
00312     Net::poll();
00313   } while(m_blockingResult == NTP_PROCESSING);
00314   Net::poll(); //Necessary for cleanup
00315   return m_blockingResult;
00316 }