The extracted NTP client from Segundos NetServices library, for use with the [[http://mbed.org/users/hlipka/libraries/NetServicesMin|NetServicesMin]] library. The only fixed bug is the memory leak / OOM problem. Needs the [[http://mbed.org/users/hlipka/libraries/DNSResolver|DNSResolver]] library as well.
Dependents: SPIVFDclock LPC1768_AppBoard_Internet_LCD_Clock
NTPClient.cpp
00001 #pragma diag_remark 1293 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 #include "dnsresolve.h" 00026 00027 #include <stdio.h> 00028 00029 //#define __DEBUG 00030 #include "dbg/dbg.h" 00031 00032 #define NTP_PORT 123 00033 #define NTP_CLIENT_PORT 0//50420 //Random port 00034 #define NTP_REQUEST_TIMEOUT 5000 00035 #define NTP_TIMESTAMP_DELTA 2208988800ull //Diff btw a UNIX timestamp (Starting Jan, 1st 1970) and a NTP timestamp (Starting Jan, 1st 1900) 00036 00037 #define htons( x ) ( (( x << 8 ) & 0xFF00) | (( x >> 8 ) & 0x00FF) ) 00038 #define ntohs( x ) (htons(x)) 00039 00040 #define htonl( x ) ( (( x << 24 ) & 0xFF000000) \ 00041 | (( x << 8 ) & 0x00FF0000) \ 00042 | (( x >> 8 ) & 0x0000FF00) \ 00043 | (( x >> 24 ) & 0x000000FF) ) 00044 #define ntohl( x ) (htonl(x)) 00045 00046 NTPClient::NTPClient() : 00047 _state(NTP_PING), 00048 _timeout(NTP_REQUEST_TIMEOUT), 00049 _closed(true), 00050 _host(), 00051 _blockingResult(NTP_PROCESSING) 00052 { 00053 _watchdog=new Timer(); 00054 DBG("\r\nNew NTPClient %p\r\n",this); 00055 } 00056 00057 NTPClient::~NTPClient() 00058 { 00059 close(); 00060 delete _watchdog; 00061 } 00062 00063 //High level setup functions 00064 NTPResult NTPClient::setTime(const Host& host) //Blocking 00065 { 00066 doSetTime(host); 00067 return blockingProcess(); 00068 } 00069 00070 void NTPClient::doSetTime(const Host& host) 00071 { 00072 init(); 00073 resetTimeout(); 00074 _host = host; 00075 if(!_host.getPort()) 00076 { 00077 _host.setPort(NTP_PORT); 00078 } 00079 if(_host.getIp().isNull()) 00080 { 00081 00082 DNSResolver *dr=new DNSResolver(); 00083 IpAddr ad=dr->resolveName(host.getName()); 00084 delete dr; 00085 if (ad.isNull()) 00086 { 00087 onResult(NTP_DNS); 00088 return; 00089 } 00090 _host.setIp(ad); 00091 } 00092 open(); 00093 } 00094 00095 void NTPClient::close() 00096 { 00097 if(_closed) 00098 return; 00099 _closed = true; //Prevent recursive calling or calling on an object being destructed by someone else 00100 _watchdog->stop(); 00101 _pUDPSocket->resetOnEvent(); 00102 _pUDPSocket->close(); 00103 delete _pUDPSocket; 00104 } 00105 00106 void NTPClient::init() //Create and setup socket if needed 00107 { 00108 if(!_closed) //Already opened 00109 return; 00110 _state = NTP_PING; 00111 _pUDPSocket = new UDPSocket; 00112 _pUDPSocket->setOnEvent(this, &NTPClient::onUDPSocketEvent); 00113 _closed = false; 00114 DBG("NTPClient: Init OK\n"); 00115 } 00116 00117 void NTPClient::open() 00118 { 00119 resetTimeout(); 00120 DBG("Opening connection\n"); 00121 _state = NTP_PING; 00122 Host localhost(IpAddr(), NTP_CLIENT_PORT, "localhost"); //Any local address 00123 _pUDPSocket->bind(localhost); 00124 if ((int)time(NULL) < 1280000000) set_time( 1280000000 ); //End of July 2010... just there to limit offset range 00125 00126 process(); 00127 00128 } 00129 00130 #define MIN(a,b) ((a)<(b))?(a):(b) 00131 void NTPClient::process() //Main state-machine 00132 { 00133 int len; 00134 Host host; 00135 switch(_state) 00136 { 00137 case NTP_PING: 00138 DBG("Ping\r\n"); 00139 //Prepare NTP Packet: 00140 _pkt.li = 0; //Leap Indicator : No warning 00141 _pkt.vn = 4; //Version Number : 4 00142 _pkt.mode = 3; //Client mode 00143 _pkt.stratum = 0; //Not relevant here 00144 _pkt.poll = 0; //Not significant as well 00145 _pkt.precision = 0; //Neither this one is 00146 00147 _pkt.rootDelay = 0; //Or this one 00148 _pkt.rootDispersion = 0; //Or that one 00149 _pkt.refId = 0; //... 00150 00151 _pkt.refTm_s = 0; 00152 _pkt.origTm_s = 0; 00153 _pkt.rxTm_s = 0; 00154 _pkt.txTm_s = htonl( NTP_TIMESTAMP_DELTA + time(NULL) ); //WARN: We are in LE format, network byte order is BE 00155 00156 _pkt.refTm_f = _pkt.origTm_f = _pkt.rxTm_f = _pkt.txTm_f = 0; 00157 00158 #ifdef __DEBUG 00159 //Hex Dump: 00160 DBG("Dump Tx:\r\n"); 00161 for(int i = 0; i< sizeof(NTPPacket); i++) 00162 { 00163 DBGL("%02x ", *((char*)&_pkt + i)); 00164 } 00165 DBGL("\r\n"); 00166 #endif 00167 00168 len = _pUDPSocket->sendto( (char*)&_pkt, sizeof(NTPPacket), &_host ); 00169 if(len < sizeof(NTPPacket)) 00170 { onResult(NTP_PRTCL); close(); return; } 00171 00172 _state = NTP_PONG; 00173 00174 break; 00175 00176 case NTP_PONG: 00177 DBG("Pong\r\n"); 00178 while( len = _pUDPSocket->recvfrom( (char*)&_pkt, sizeof(NTPPacket), &host ) ) 00179 { 00180 if( len <= 0 ) 00181 break; 00182 if( !host.getIp().isEq(_host.getIp()) ) 00183 continue; //Not our packet 00184 if( len > 0 ) 00185 break; 00186 } 00187 00188 if(len == 0) 00189 return; //Wait for the next packet 00190 00191 if(len < 0) 00192 { onResult(NTP_PRTCL); close(); return; } 00193 00194 if(len < sizeof(NTPPacket)) //TODO: Accept chunks 00195 { onResult(NTP_PRTCL); close(); return; } 00196 00197 #ifdef __DEBUG 00198 //Hex Dump: 00199 DBG("Dump Rx:\r\n"); 00200 for(int i = 0; i< sizeof(NTPPacket); i++) 00201 { 00202 DBGL("%02x ", *((char*)&_pkt + i)); 00203 } 00204 DBGL("\r\n"); 00205 #endif 00206 00207 if( _pkt.stratum == 0) //Kiss of death message : Not good ! 00208 { 00209 onResult(NTP_PRTCL); close(); return; 00210 } 00211 00212 //Correct Endianness 00213 _pkt.refTm_s = ntohl( _pkt.refTm_s ); 00214 _pkt.refTm_f = ntohl( _pkt.refTm_f ); 00215 _pkt.origTm_s = ntohl( _pkt.origTm_s ); 00216 _pkt.origTm_f = ntohl( _pkt.origTm_f ); 00217 _pkt.rxTm_s = ntohl( _pkt.rxTm_s ); 00218 _pkt.rxTm_f = ntohl( _pkt.rxTm_f ); 00219 _pkt.txTm_s = ntohl( _pkt.txTm_s ); 00220 _pkt.txTm_f = ntohl( _pkt.txTm_f ); 00221 00222 //Compute offset, see RFC 4330 p.13 00223 uint32_t destTm_s = (NTP_TIMESTAMP_DELTA + time(NULL)); 00224 //int32_t origTm = (int32_t) ((uint64_t) _pkt.origTm - NTP_TIMESTAMP_DELTA); //Convert in local 32 bits timestamps 00225 //int32_t rxTm = (int32_t) ((uint64_t) _pkt.rxTm - NTP_TIMESTAMP_DELTA); //Convert in local 32 bits timestamps 00226 //int32_t txTm = (int32_t) ((uint64_t) _pkt.txTm - NTP_TIMESTAMP_DELTA); //Convert in local 32 bits timestamps 00227 // int64_t offset = ( ( ( _pkt.rxT_s - m_pkt.origTm_s ) + ( m_pkt.txT_s - destTm_s ) ) << 32 + ( ( m_pkt.rxTm_f - m_pkt.origTm_f ) + ( m_pkt.txT_f - 0 ) ) ) / 2; 00228 int64_t offset = ( (int64_t)( _pkt.rxTm_s - _pkt.origTm_s ) + (int64_t) ( _pkt.txTm_s - destTm_s ) ) / 2; //Avoid overflow 00229 DBG("Sent @%d\r\n", _pkt.txTm_s); 00230 DBG("Offset: %d\r\n", offset); 00231 00232 //Set time accordingly 00233 set_time( time(NULL) + (offset /*>> 32*/) ); 00234 00235 onResult(NTP_OK); 00236 close(); 00237 break; 00238 } 00239 } 00240 00241 void NTPClient::setTimeout(int ms) 00242 { 00243 _timeout = ms; 00244 } 00245 00246 void NTPClient::resetTimeout() 00247 { 00248 _watchdog->reset(); 00249 _watchdog->start(); 00250 } 00251 00252 void NTPClient::onTimeout() //Connection has timed out 00253 { 00254 close(); 00255 onResult(NTP_TIMEOUT); 00256 } 00257 00258 void NTPClient::onUDPSocketEvent(UDPSocketEvent e) 00259 { 00260 resetTimeout(); 00261 switch(e) 00262 { 00263 case UDPSOCKET_READABLE: //The only event for now 00264 process(); 00265 break; 00266 } 00267 } 00268 00269 void NTPClient::onResult(NTPResult r) //Must be called by impl when the request completes 00270 { 00271 _blockingResult = r; //Blocking mode 00272 } 00273 00274 NTPResult NTPClient::blockingProcess() //Called in blocking mode, calls Net::poll() until return code is available 00275 { 00276 _blockingResult = NTP_PROCESSING; 00277 do 00278 { 00279 Net::poll(); 00280 wait_us(100); 00281 if (_watchdog->read_ms()>_timeout) 00282 return NTP_TIMEOUT; 00283 } while(_blockingResult == NTP_PROCESSING); 00284 00285 Net::poll(); //Necessary for cleanup 00286 00287 return _blockingResult; 00288 }
Generated on Wed Jul 13 2022 20:04:13 by 1.7.2