Dirk-Willem van Gulik (NXP/mbed)
/
Bonjour
Bonjour/Zerconf library
Embed:
(wiki syntax)
Show/hide line numbers
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 while((len = m_pUDPSocket->recvfrom( (char*)&m_pkt, sizeof(NTPPacket), &host )) != 0 ) 00201 { 00202 if( len <= 0 ) 00203 break; 00204 if( !host.getIp().isEq(m_host.getIp()) ) 00205 continue; //Not our packet 00206 if( len > 0 ) 00207 break; 00208 } 00209 00210 if(len == 0) 00211 return; //Wait for the next packet 00212 00213 if(len < 0) 00214 { onResult(NTP_PRTCL); close(); return; } 00215 00216 if(len < sizeof(NTPPacket)) //TODO: Accept chunks 00217 { onResult(NTP_PRTCL); close(); return; } 00218 00219 //Hex Dump: 00220 DBG("\r\nDump Rx:\r\n"); 00221 for(int i = 0; i< sizeof(NTPPacket); i++) 00222 { 00223 DBG("%02x ", *((char*)&m_pkt + i)); 00224 } 00225 DBG("\r\n\r\n"); 00226 00227 if( m_pkt.stratum == 0) //Kiss of death message : Not good ! 00228 { 00229 onResult(NTP_PRTCL); close(); return; 00230 } 00231 00232 //Correct Endianness 00233 m_pkt.refTm_s = ntohl( m_pkt.refTm_s ); 00234 m_pkt.refTm_f = ntohl( m_pkt.refTm_f ); 00235 m_pkt.origTm_s = ntohl( m_pkt.origTm_s ); 00236 m_pkt.origTm_f = ntohl( m_pkt.origTm_f ); 00237 m_pkt.rxTm_s = ntohl( m_pkt.rxTm_s ); 00238 m_pkt.rxTm_f = ntohl( m_pkt.rxTm_f ); 00239 m_pkt.txTm_s = ntohl( m_pkt.txTm_s ); 00240 m_pkt.txTm_f = ntohl( m_pkt.txTm_f ); 00241 00242 //Compute offset, see RFC 4330 p.13 00243 uint32_t destTm_s = (NTP_TIMESTAMP_DELTA + time(NULL)); 00244 //int32_t origTm = (int32_t) ((uint64_t) m_pkt.origTm - NTP_TIMESTAMP_DELTA); //Convert in local 32 bits timestamps 00245 //int32_t rxTm = (int32_t) ((uint64_t) m_pkt.rxTm - NTP_TIMESTAMP_DELTA); //Convert in local 32 bits timestamps 00246 //int32_t txTm = (int32_t) ((uint64_t) m_pkt.txTm - NTP_TIMESTAMP_DELTA); //Convert in local 32 bits timestamps 00247 // 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; 00248 int64_t offset = ( (int64_t)( m_pkt.rxTm_s - m_pkt.origTm_s ) + (int64_t) ( m_pkt.txTm_s - destTm_s ) ) / 2; //Avoid overflow 00249 DBG("\r\nSent @%d\r\n", m_pkt.txTm_s); 00250 DBG("\r\nOffset: %d\r\n", offset); 00251 //Set time accordingly 00252 set_time( time(NULL) + (offset /*>> 32*/) ); 00253 00254 onResult(NTP_OK); 00255 close(); 00256 00257 break; 00258 } 00259 } 00260 00261 void NTPClient::setTimeout(int ms) 00262 { 00263 m_timeout = ms; 00264 } 00265 00266 void NTPClient::resetTimeout() 00267 { 00268 m_watchdog.reset(); 00269 m_watchdog.start(); 00270 } 00271 00272 void NTPClient::onTimeout() //Connection has timed out 00273 { 00274 close(); 00275 onResult(NTP_TIMEOUT); 00276 } 00277 00278 void NTPClient::onDNSReply(DNSReply r) 00279 { 00280 if(m_closed) 00281 { 00282 DBG("\r\nWARN: Discarded\r\n"); 00283 return; 00284 } 00285 00286 if( r != DNS_FOUND ) 00287 { 00288 DBG("\r\nCould not resolve hostname.\r\n"); 00289 onResult(NTP_DNS); 00290 close(); 00291 return; 00292 } 00293 DBG("\r\nDNS resolved.\r\n"); 00294 m_pDnsReq->close(); 00295 delete m_pDnsReq; 00296 m_pDnsReq=NULL; 00297 open(); 00298 } 00299 00300 void NTPClient::onUDPSocketEvent(UDPSocketEvent e) 00301 { 00302 resetTimeout(); 00303 switch(e) 00304 { 00305 case UDPSOCKET_READABLE: //The only event for now 00306 resetTimeout(); 00307 process(); 00308 break; 00309 } 00310 } 00311 00312 void NTPClient::onResult(NTPResult r) //Must be called by impl when the request completes 00313 { 00314 if(m_pCbItem && m_pCbMeth) 00315 (m_pCbItem->*m_pCbMeth)(r); 00316 else if(m_pCb) 00317 m_pCb(r); 00318 m_blockingResult = r; //Blocking mode 00319 } 00320 00321 NTPResult NTPClient::blockingProcess() //Called in blocking mode, calls Net::poll() until return code is available 00322 { 00323 m_blockingResult = NTP_PROCESSING; 00324 do 00325 { 00326 Net::poll(); 00327 } while(m_blockingResult == NTP_PROCESSING); 00328 Net::poll(); //Necessary for cleanup 00329 return m_blockingResult; 00330 }
Generated on Tue Jul 12 2022 18:38:37 by 1.7.2