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

Committer:
hlipka
Date:
Mon Jan 24 23:07:27 2011 +0000
Revision:
1:63ded11b8fa2
Parent:
0:ebea15f18f84
bugfix: re-added timeout handling

Who changed what in which revision?

UserRevisionLine numberNew contents of line
hlipka 0:ebea15f18f84 1 #pragma diag_remark 1293
hlipka 0:ebea15f18f84 2 /*
hlipka 0:ebea15f18f84 3 Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
hlipka 0:ebea15f18f84 4
hlipka 0:ebea15f18f84 5 Permission is hereby granted, free of charge, to any person obtaining a copy
hlipka 0:ebea15f18f84 6 of this software and associated documentation files (the "Software"), to deal
hlipka 0:ebea15f18f84 7 in the Software without restriction, including without limitation the rights
hlipka 0:ebea15f18f84 8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
hlipka 0:ebea15f18f84 9 copies of the Software, and to permit persons to whom the Software is
hlipka 0:ebea15f18f84 10 furnished to do so, subject to the following conditions:
hlipka 0:ebea15f18f84 11
hlipka 0:ebea15f18f84 12 The above copyright notice and this permission notice shall be included in
hlipka 0:ebea15f18f84 13 all copies or substantial portions of the Software.
hlipka 0:ebea15f18f84 14
hlipka 0:ebea15f18f84 15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
hlipka 0:ebea15f18f84 16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
hlipka 0:ebea15f18f84 17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
hlipka 0:ebea15f18f84 18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
hlipka 0:ebea15f18f84 19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
hlipka 0:ebea15f18f84 20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
hlipka 0:ebea15f18f84 21 THE SOFTWARE.
hlipka 0:ebea15f18f84 22 */
hlipka 0:ebea15f18f84 23
hlipka 0:ebea15f18f84 24 #include "NTPClient.h"
hlipka 1:63ded11b8fa2 25 #include "dnsresolve.h"
hlipka 0:ebea15f18f84 26
hlipka 0:ebea15f18f84 27 #include <stdio.h>
hlipka 0:ebea15f18f84 28
hlipka 0:ebea15f18f84 29 //#define __DEBUG
hlipka 0:ebea15f18f84 30 #include "dbg/dbg.h"
hlipka 0:ebea15f18f84 31
hlipka 0:ebea15f18f84 32 #define NTP_PORT 123
hlipka 0:ebea15f18f84 33 #define NTP_CLIENT_PORT 0//50420 //Random port
hlipka 1:63ded11b8fa2 34 #define NTP_REQUEST_TIMEOUT 5000
hlipka 0:ebea15f18f84 35 #define NTP_TIMESTAMP_DELTA 2208988800ull //Diff btw a UNIX timestamp (Starting Jan, 1st 1970) and a NTP timestamp (Starting Jan, 1st 1900)
hlipka 0:ebea15f18f84 36
hlipka 0:ebea15f18f84 37 #define htons( x ) ( (( x << 8 ) & 0xFF00) | (( x >> 8 ) & 0x00FF) )
hlipka 0:ebea15f18f84 38 #define ntohs( x ) (htons(x))
hlipka 0:ebea15f18f84 39
hlipka 0:ebea15f18f84 40 #define htonl( x ) ( (( x << 24 ) & 0xFF000000) \
hlipka 0:ebea15f18f84 41 | (( x << 8 ) & 0x00FF0000) \
hlipka 0:ebea15f18f84 42 | (( x >> 8 ) & 0x0000FF00) \
hlipka 0:ebea15f18f84 43 | (( x >> 24 ) & 0x000000FF) )
hlipka 0:ebea15f18f84 44 #define ntohl( x ) (htonl(x))
hlipka 0:ebea15f18f84 45
hlipka 0:ebea15f18f84 46 NTPClient::NTPClient() :
hlipka 1:63ded11b8fa2 47 _state(NTP_PING),
hlipka 1:63ded11b8fa2 48 _timeout(NTP_REQUEST_TIMEOUT),
hlipka 1:63ded11b8fa2 49 _closed(true),
hlipka 1:63ded11b8fa2 50 _host(),
hlipka 1:63ded11b8fa2 51 _blockingResult(NTP_PROCESSING)
hlipka 0:ebea15f18f84 52 {
hlipka 1:63ded11b8fa2 53 _watchdog=new Timer();
hlipka 0:ebea15f18f84 54 DBG("\r\nNew NTPClient %p\r\n",this);
hlipka 0:ebea15f18f84 55 }
hlipka 0:ebea15f18f84 56
hlipka 0:ebea15f18f84 57 NTPClient::~NTPClient()
hlipka 0:ebea15f18f84 58 {
hlipka 0:ebea15f18f84 59 close();
hlipka 1:63ded11b8fa2 60 delete _watchdog;
hlipka 0:ebea15f18f84 61 }
hlipka 0:ebea15f18f84 62
hlipka 0:ebea15f18f84 63 //High level setup functions
hlipka 0:ebea15f18f84 64 NTPResult NTPClient::setTime(const Host& host) //Blocking
hlipka 0:ebea15f18f84 65 {
hlipka 0:ebea15f18f84 66 doSetTime(host);
hlipka 0:ebea15f18f84 67 return blockingProcess();
hlipka 0:ebea15f18f84 68 }
hlipka 0:ebea15f18f84 69
hlipka 0:ebea15f18f84 70 void NTPClient::doSetTime(const Host& host)
hlipka 0:ebea15f18f84 71 {
hlipka 0:ebea15f18f84 72 init();
hlipka 0:ebea15f18f84 73 resetTimeout();
hlipka 1:63ded11b8fa2 74 _host = host;
hlipka 1:63ded11b8fa2 75 if(!_host.getPort())
hlipka 0:ebea15f18f84 76 {
hlipka 1:63ded11b8fa2 77 _host.setPort(NTP_PORT);
hlipka 0:ebea15f18f84 78 }
hlipka 1:63ded11b8fa2 79 if(_host.getIp().isNull())
hlipka 0:ebea15f18f84 80 {
hlipka 1:63ded11b8fa2 81
hlipka 1:63ded11b8fa2 82 DNSResolver *dr=new DNSResolver();
hlipka 1:63ded11b8fa2 83 IpAddr ad=dr->resolveName(host.getName());
hlipka 1:63ded11b8fa2 84 delete dr;
hlipka 1:63ded11b8fa2 85 if (ad.isNull())
hlipka 1:63ded11b8fa2 86 {
hlipka 1:63ded11b8fa2 87 onResult(NTP_DNS);
hlipka 1:63ded11b8fa2 88 return;
hlipka 1:63ded11b8fa2 89 }
hlipka 1:63ded11b8fa2 90 _host.setIp(ad);
hlipka 0:ebea15f18f84 91 }
hlipka 0:ebea15f18f84 92 open();
hlipka 0:ebea15f18f84 93 }
hlipka 0:ebea15f18f84 94
hlipka 0:ebea15f18f84 95 void NTPClient::close()
hlipka 0:ebea15f18f84 96 {
hlipka 1:63ded11b8fa2 97 if(_closed)
hlipka 0:ebea15f18f84 98 return;
hlipka 1:63ded11b8fa2 99 _closed = true; //Prevent recursive calling or calling on an object being destructed by someone else
hlipka 1:63ded11b8fa2 100 _watchdog->stop();
hlipka 1:63ded11b8fa2 101 _pUDPSocket->resetOnEvent();
hlipka 1:63ded11b8fa2 102 _pUDPSocket->close();
hlipka 1:63ded11b8fa2 103 delete _pUDPSocket;
hlipka 0:ebea15f18f84 104 }
hlipka 0:ebea15f18f84 105
hlipka 0:ebea15f18f84 106 void NTPClient::init() //Create and setup socket if needed
hlipka 0:ebea15f18f84 107 {
hlipka 1:63ded11b8fa2 108 if(!_closed) //Already opened
hlipka 0:ebea15f18f84 109 return;
hlipka 1:63ded11b8fa2 110 _state = NTP_PING;
hlipka 1:63ded11b8fa2 111 _pUDPSocket = new UDPSocket;
hlipka 1:63ded11b8fa2 112 _pUDPSocket->setOnEvent(this, &NTPClient::onUDPSocketEvent);
hlipka 1:63ded11b8fa2 113 _closed = false;
hlipka 0:ebea15f18f84 114 DBG("NTPClient: Init OK\n");
hlipka 0:ebea15f18f84 115 }
hlipka 0:ebea15f18f84 116
hlipka 0:ebea15f18f84 117 void NTPClient::open()
hlipka 0:ebea15f18f84 118 {
hlipka 0:ebea15f18f84 119 resetTimeout();
hlipka 0:ebea15f18f84 120 DBG("Opening connection\n");
hlipka 1:63ded11b8fa2 121 _state = NTP_PING;
hlipka 0:ebea15f18f84 122 Host localhost(IpAddr(), NTP_CLIENT_PORT, "localhost"); //Any local address
hlipka 1:63ded11b8fa2 123 _pUDPSocket->bind(localhost);
hlipka 0:ebea15f18f84 124 if ((int)time(NULL) < 1280000000) set_time( 1280000000 ); //End of July 2010... just there to limit offset range
hlipka 0:ebea15f18f84 125
hlipka 0:ebea15f18f84 126 process();
hlipka 0:ebea15f18f84 127
hlipka 0:ebea15f18f84 128 }
hlipka 0:ebea15f18f84 129
hlipka 0:ebea15f18f84 130 #define MIN(a,b) ((a)<(b))?(a):(b)
hlipka 0:ebea15f18f84 131 void NTPClient::process() //Main state-machine
hlipka 0:ebea15f18f84 132 {
hlipka 0:ebea15f18f84 133 int len;
hlipka 0:ebea15f18f84 134 Host host;
hlipka 1:63ded11b8fa2 135 switch(_state)
hlipka 0:ebea15f18f84 136 {
hlipka 0:ebea15f18f84 137 case NTP_PING:
hlipka 0:ebea15f18f84 138 DBG("Ping\r\n");
hlipka 0:ebea15f18f84 139 //Prepare NTP Packet:
hlipka 1:63ded11b8fa2 140 _pkt.li = 0; //Leap Indicator : No warning
hlipka 1:63ded11b8fa2 141 _pkt.vn = 4; //Version Number : 4
hlipka 1:63ded11b8fa2 142 _pkt.mode = 3; //Client mode
hlipka 1:63ded11b8fa2 143 _pkt.stratum = 0; //Not relevant here
hlipka 1:63ded11b8fa2 144 _pkt.poll = 0; //Not significant as well
hlipka 1:63ded11b8fa2 145 _pkt.precision = 0; //Neither this one is
hlipka 0:ebea15f18f84 146
hlipka 1:63ded11b8fa2 147 _pkt.rootDelay = 0; //Or this one
hlipka 1:63ded11b8fa2 148 _pkt.rootDispersion = 0; //Or that one
hlipka 1:63ded11b8fa2 149 _pkt.refId = 0; //...
hlipka 0:ebea15f18f84 150
hlipka 1:63ded11b8fa2 151 _pkt.refTm_s = 0;
hlipka 1:63ded11b8fa2 152 _pkt.origTm_s = 0;
hlipka 1:63ded11b8fa2 153 _pkt.rxTm_s = 0;
hlipka 1:63ded11b8fa2 154 _pkt.txTm_s = htonl( NTP_TIMESTAMP_DELTA + time(NULL) ); //WARN: We are in LE format, network byte order is BE
hlipka 0:ebea15f18f84 155
hlipka 1:63ded11b8fa2 156 _pkt.refTm_f = _pkt.origTm_f = _pkt.rxTm_f = _pkt.txTm_f = 0;
hlipka 0:ebea15f18f84 157
hlipka 0:ebea15f18f84 158 #ifdef __DEBUG
hlipka 0:ebea15f18f84 159 //Hex Dump:
hlipka 0:ebea15f18f84 160 DBG("Dump Tx:\r\n");
hlipka 0:ebea15f18f84 161 for(int i = 0; i< sizeof(NTPPacket); i++)
hlipka 0:ebea15f18f84 162 {
hlipka 1:63ded11b8fa2 163 DBGL("%02x ", *((char*)&_pkt + i));
hlipka 0:ebea15f18f84 164 }
hlipka 0:ebea15f18f84 165 DBGL("\r\n");
hlipka 0:ebea15f18f84 166 #endif
hlipka 0:ebea15f18f84 167
hlipka 1:63ded11b8fa2 168 len = _pUDPSocket->sendto( (char*)&_pkt, sizeof(NTPPacket), &_host );
hlipka 0:ebea15f18f84 169 if(len < sizeof(NTPPacket))
hlipka 0:ebea15f18f84 170 { onResult(NTP_PRTCL); close(); return; }
hlipka 0:ebea15f18f84 171
hlipka 1:63ded11b8fa2 172 _state = NTP_PONG;
hlipka 0:ebea15f18f84 173
hlipka 0:ebea15f18f84 174 break;
hlipka 0:ebea15f18f84 175
hlipka 0:ebea15f18f84 176 case NTP_PONG:
hlipka 0:ebea15f18f84 177 DBG("Pong\r\n");
hlipka 1:63ded11b8fa2 178 while( len = _pUDPSocket->recvfrom( (char*)&_pkt, sizeof(NTPPacket), &host ) )
hlipka 0:ebea15f18f84 179 {
hlipka 0:ebea15f18f84 180 if( len <= 0 )
hlipka 0:ebea15f18f84 181 break;
hlipka 1:63ded11b8fa2 182 if( !host.getIp().isEq(_host.getIp()) )
hlipka 0:ebea15f18f84 183 continue; //Not our packet
hlipka 0:ebea15f18f84 184 if( len > 0 )
hlipka 0:ebea15f18f84 185 break;
hlipka 0:ebea15f18f84 186 }
hlipka 0:ebea15f18f84 187
hlipka 0:ebea15f18f84 188 if(len == 0)
hlipka 0:ebea15f18f84 189 return; //Wait for the next packet
hlipka 0:ebea15f18f84 190
hlipka 0:ebea15f18f84 191 if(len < 0)
hlipka 0:ebea15f18f84 192 { onResult(NTP_PRTCL); close(); return; }
hlipka 0:ebea15f18f84 193
hlipka 0:ebea15f18f84 194 if(len < sizeof(NTPPacket)) //TODO: Accept chunks
hlipka 0:ebea15f18f84 195 { onResult(NTP_PRTCL); close(); return; }
hlipka 0:ebea15f18f84 196
hlipka 0:ebea15f18f84 197 #ifdef __DEBUG
hlipka 0:ebea15f18f84 198 //Hex Dump:
hlipka 0:ebea15f18f84 199 DBG("Dump Rx:\r\n");
hlipka 0:ebea15f18f84 200 for(int i = 0; i< sizeof(NTPPacket); i++)
hlipka 0:ebea15f18f84 201 {
hlipka 1:63ded11b8fa2 202 DBGL("%02x ", *((char*)&_pkt + i));
hlipka 0:ebea15f18f84 203 }
hlipka 0:ebea15f18f84 204 DBGL("\r\n");
hlipka 0:ebea15f18f84 205 #endif
hlipka 0:ebea15f18f84 206
hlipka 1:63ded11b8fa2 207 if( _pkt.stratum == 0) //Kiss of death message : Not good !
hlipka 0:ebea15f18f84 208 {
hlipka 0:ebea15f18f84 209 onResult(NTP_PRTCL); close(); return;
hlipka 0:ebea15f18f84 210 }
hlipka 0:ebea15f18f84 211
hlipka 0:ebea15f18f84 212 //Correct Endianness
hlipka 1:63ded11b8fa2 213 _pkt.refTm_s = ntohl( _pkt.refTm_s );
hlipka 1:63ded11b8fa2 214 _pkt.refTm_f = ntohl( _pkt.refTm_f );
hlipka 1:63ded11b8fa2 215 _pkt.origTm_s = ntohl( _pkt.origTm_s );
hlipka 1:63ded11b8fa2 216 _pkt.origTm_f = ntohl( _pkt.origTm_f );
hlipka 1:63ded11b8fa2 217 _pkt.rxTm_s = ntohl( _pkt.rxTm_s );
hlipka 1:63ded11b8fa2 218 _pkt.rxTm_f = ntohl( _pkt.rxTm_f );
hlipka 1:63ded11b8fa2 219 _pkt.txTm_s = ntohl( _pkt.txTm_s );
hlipka 1:63ded11b8fa2 220 _pkt.txTm_f = ntohl( _pkt.txTm_f );
hlipka 0:ebea15f18f84 221
hlipka 0:ebea15f18f84 222 //Compute offset, see RFC 4330 p.13
hlipka 0:ebea15f18f84 223 uint32_t destTm_s = (NTP_TIMESTAMP_DELTA + time(NULL));
hlipka 1:63ded11b8fa2 224 //int32_t origTm = (int32_t) ((uint64_t) _pkt.origTm - NTP_TIMESTAMP_DELTA); //Convert in local 32 bits timestamps
hlipka 1:63ded11b8fa2 225 //int32_t rxTm = (int32_t) ((uint64_t) _pkt.rxTm - NTP_TIMESTAMP_DELTA); //Convert in local 32 bits timestamps
hlipka 1:63ded11b8fa2 226 //int32_t txTm = (int32_t) ((uint64_t) _pkt.txTm - NTP_TIMESTAMP_DELTA); //Convert in local 32 bits timestamps
hlipka 1:63ded11b8fa2 227 // 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;
hlipka 1:63ded11b8fa2 228 int64_t offset = ( (int64_t)( _pkt.rxTm_s - _pkt.origTm_s ) + (int64_t) ( _pkt.txTm_s - destTm_s ) ) / 2; //Avoid overflow
hlipka 1:63ded11b8fa2 229 DBG("Sent @%d\r\n", _pkt.txTm_s);
hlipka 0:ebea15f18f84 230 DBG("Offset: %d\r\n", offset);
hlipka 0:ebea15f18f84 231
hlipka 0:ebea15f18f84 232 //Set time accordingly
hlipka 0:ebea15f18f84 233 set_time( time(NULL) + (offset /*>> 32*/) );
hlipka 0:ebea15f18f84 234
hlipka 0:ebea15f18f84 235 onResult(NTP_OK);
hlipka 0:ebea15f18f84 236 close();
hlipka 0:ebea15f18f84 237 break;
hlipka 0:ebea15f18f84 238 }
hlipka 0:ebea15f18f84 239 }
hlipka 0:ebea15f18f84 240
hlipka 0:ebea15f18f84 241 void NTPClient::setTimeout(int ms)
hlipka 0:ebea15f18f84 242 {
hlipka 1:63ded11b8fa2 243 _timeout = ms;
hlipka 0:ebea15f18f84 244 }
hlipka 0:ebea15f18f84 245
hlipka 0:ebea15f18f84 246 void NTPClient::resetTimeout()
hlipka 0:ebea15f18f84 247 {
hlipka 1:63ded11b8fa2 248 _watchdog->reset();
hlipka 1:63ded11b8fa2 249 _watchdog->start();
hlipka 0:ebea15f18f84 250 }
hlipka 0:ebea15f18f84 251
hlipka 0:ebea15f18f84 252 void NTPClient::onTimeout() //Connection has timed out
hlipka 0:ebea15f18f84 253 {
hlipka 0:ebea15f18f84 254 close();
hlipka 0:ebea15f18f84 255 onResult(NTP_TIMEOUT);
hlipka 0:ebea15f18f84 256 }
hlipka 0:ebea15f18f84 257
hlipka 0:ebea15f18f84 258 void NTPClient::onUDPSocketEvent(UDPSocketEvent e)
hlipka 0:ebea15f18f84 259 {
hlipka 0:ebea15f18f84 260 resetTimeout();
hlipka 0:ebea15f18f84 261 switch(e)
hlipka 0:ebea15f18f84 262 {
hlipka 0:ebea15f18f84 263 case UDPSOCKET_READABLE: //The only event for now
hlipka 0:ebea15f18f84 264 process();
hlipka 0:ebea15f18f84 265 break;
hlipka 0:ebea15f18f84 266 }
hlipka 0:ebea15f18f84 267 }
hlipka 0:ebea15f18f84 268
hlipka 0:ebea15f18f84 269 void NTPClient::onResult(NTPResult r) //Must be called by impl when the request completes
hlipka 0:ebea15f18f84 270 {
hlipka 1:63ded11b8fa2 271 _blockingResult = r; //Blocking mode
hlipka 0:ebea15f18f84 272 }
hlipka 0:ebea15f18f84 273
hlipka 0:ebea15f18f84 274 NTPResult NTPClient::blockingProcess() //Called in blocking mode, calls Net::poll() until return code is available
hlipka 0:ebea15f18f84 275 {
hlipka 1:63ded11b8fa2 276 _blockingResult = NTP_PROCESSING;
hlipka 0:ebea15f18f84 277 do
hlipka 0:ebea15f18f84 278 {
hlipka 0:ebea15f18f84 279 Net::poll();
hlipka 1:63ded11b8fa2 280 wait_us(100);
hlipka 1:63ded11b8fa2 281 if (_watchdog->read_ms()>_timeout)
hlipka 1:63ded11b8fa2 282 return NTP_TIMEOUT;
hlipka 1:63ded11b8fa2 283 } while(_blockingResult == NTP_PROCESSING);
hlipka 0:ebea15f18f84 284
hlipka 0:ebea15f18f84 285 Net::poll(); //Necessary for cleanup
hlipka 0:ebea15f18f84 286
hlipka 1:63ded11b8fa2 287 return _blockingResult;
hlipka 0:ebea15f18f84 288 }