Darran Shepherd
/
AutoIpNetStack
Net stack with AutoIP enabled
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 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 }
Generated on Tue Jul 12 2022 15:37:04 by 1.7.2