Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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 }
Generated on Tue Jul 12 2022 21:10:25 by
1.7.2