Donatien Garnier / NTPClientLib
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers NTPClient.cpp Source File

NTPClient.cpp

00001 /* NTPClient.cpp */
00002 /*
00003 Copyright (C) 2012 ARM Limited.
00004 
00005 Permission is hereby granted, free of charge, to any person obtaining a copy of
00006 this software and associated documentation files (the "Software"), to deal in
00007 the Software without restriction, including without limitation the rights to
00008 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
00009 of the Software, and to permit persons to whom the Software is furnished to do
00010 so, subject to the following conditions:
00011 
00012 The above copyright notice and this permission notice shall be included in all
00013 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 THE
00021 SOFTWARE.
00022 */
00023 
00024 #define __DEBUG__ 4 //Maximum verbosity
00025 #ifndef __MODULE__
00026 #define __MODULE__ "NTPClient.cpp"
00027 #endif
00028 
00029 #include "core/fwk.h"
00030 
00031 #include "NTPClient.h"
00032 
00033 #include "mbed.h" //time() and set_time()
00034 
00035 #define NTP_PORT 123
00036 #define NTP_CLIENT_PORT 0 //Random port
00037 #define NTP_REQUEST_TIMEOUT 15000
00038 #define NTP_TIMESTAMP_DELTA 2208988800ull //Diff btw a UNIX timestamp (Starting Jan, 1st 1970) and a NTP timestamp (Starting Jan, 1st 1900)
00039 
00040 NTPClient::NTPClient()
00041 {
00042 
00043 
00044 }
00045 
00046 int NTPClient::setTime(const char* host, uint16_t port, uint32_t timeout)
00047 {
00048   struct sockaddr_in serverAddr;
00049 
00050   std::memset(&serverAddr, 0, sizeof(struct sockaddr_in));
00051 
00052 #if __DEBUG__ >= 3
00053   time_t ctTime;
00054   ctTime = time(NULL);
00055   INFO("Time is set to (UTC): %s", ctime(&ctTime));
00056 #endif
00057 
00058   //Resolve DNS if needed
00059   if(serverAddr.sin_addr.s_addr == 0)
00060   {
00061     DBG("Resolving DNS address or populate hard-coded IP address");
00062     struct hostent *server = socket::gethostbyname(host);
00063     if(server == NULL)
00064     {
00065       return NET_NOTFOUND; //Fail
00066     }
00067     memcpy((char*)&serverAddr.sin_addr.s_addr, (char*)server->h_addr_list[0], server->h_length);
00068   }
00069 
00070   serverAddr.sin_family = AF_INET;
00071   serverAddr.sin_port = htons(port);
00072 
00073   //Now create & bind socket
00074   DBG("Creating socket");
00075   int sock  = socket::socket(AF_INET, SOCK_DGRAM, 0); //UDP socket
00076   if (sock < 0)
00077   {
00078     ERR("Could not create socket");
00079     return NET_OOM;
00080   }
00081   DBG("Handle is %d",sock);
00082 
00083   //Create local address
00084   struct sockaddr_in localhostAddr;
00085 
00086   std::memset(&localhostAddr, 0, sizeof(struct sockaddr_in));
00087 
00088   localhostAddr.sin_family = AF_INET;
00089   localhostAddr.sin_port = htons(NTP_CLIENT_PORT); //Random port
00090   localhostAddr.sin_addr.s_addr = htonl(INADDR_ANY); //Any local address
00091 
00092   if ( socket::bind( sock, (struct sockaddr*)&localhostAddr, sizeof(localhostAddr)) < 0 ) //Listen on local port
00093   {
00094     ERR("Could not bind socket");
00095     socket::close(sock);
00096     return NET_OOM;
00097   }
00098 
00099   struct NTPPacket pkt;
00100 
00101   //Now ping the server and wait for response
00102   DBG("Ping");
00103   //Prepare NTP Packet:
00104   pkt.li = 0; //Leap Indicator : No warning
00105   pkt.vn = 4; //Version Number : 4
00106   pkt.mode = 3; //Client mode
00107   pkt.stratum = 0; //Not relevant here
00108   pkt.poll = 0; //Not significant as well
00109   pkt.precision = 0; //Neither this one is
00110 
00111   pkt.rootDelay = 0; //Or this one
00112   pkt.rootDispersion = 0; //Or that one
00113   pkt.refId = 0; //...
00114 
00115   pkt.refTm_s = 0;
00116   pkt.origTm_s = 0;
00117   pkt.rxTm_s = 0;
00118   pkt.txTm_s = htonl( NTP_TIMESTAMP_DELTA + time(NULL) ); //WARN: We are in LE format, network byte order is BE
00119 
00120   pkt.refTm_f = pkt.origTm_f = pkt.rxTm_f = pkt.txTm_f = 0;
00121 
00122   //Set timeout, non-blocking and wait using select
00123   if( socket::sendto( sock, (void*)&pkt, sizeof(NTPPacket), 0, (struct sockaddr*)&serverAddr, sizeof(serverAddr) ) < 0 )
00124   {
00125     ERR("Could not send packet");
00126     socket::close(sock);
00127     return NET_CONN;
00128   }
00129 
00130   //Wait for socket to be readable
00131   //Creating FS set
00132   fd_set socksSet;
00133   FD_ZERO(&socksSet);
00134   FD_SET(sock, &socksSet);
00135   struct timeval t_val;
00136   t_val.tv_sec = timeout / 1000;
00137   t_val.tv_usec = (timeout - (t_val.tv_sec * 1000)) * 1000;
00138   int ret = socket::select(FD_SETSIZE, &socksSet, NULL, NULL, &t_val);
00139   if(ret <= 0 || !FD_ISSET(sock, &socksSet))
00140   {
00141     ERR("Timeout while waiting for answer");
00142     socket::close(sock);
00143     return NET_TIMEOUT; //Timeout
00144   }
00145 
00146   //Read response
00147   DBG("Pong");
00148   struct sockaddr_in respAddr;
00149   socklen_t respAddrLen = sizeof(respAddr);
00150   do
00151   {
00152     ret = socket::recvfrom( sock, (void*)&pkt, sizeof(NTPPacket), 0, (struct sockaddr*)&respAddr, &respAddrLen);
00153     if(ret < 0)
00154     {
00155       ERR("Could not receive packet");
00156       socket::close(sock);
00157       return NET_CONN;
00158     }
00159   } while( respAddr.sin_addr.s_addr != serverAddr.sin_addr.s_addr);
00160 
00161   if(ret < sizeof(NTPPacket)) //TODO: Accept chunks
00162   {
00163     ERR("Receive packet size does not match");
00164     socket::close(sock);
00165     return NET_PROTOCOL;
00166   }
00167 
00168   if( pkt.stratum == 0)  //Kiss of death message : Not good !
00169   {
00170     ERR("Kissed to death!");
00171     socket::close(sock);
00172     return NTP_PORT;
00173   }
00174 
00175   //Correct Endianness
00176   pkt.refTm_s = ntohl( pkt.refTm_s );
00177   pkt.refTm_f = ntohl( pkt.refTm_f );
00178   pkt.origTm_s = ntohl( pkt.origTm_s );
00179   pkt.origTm_f = ntohl( pkt.origTm_f );
00180   pkt.rxTm_s = ntohl( pkt.rxTm_s );
00181   pkt.rxTm_f = ntohl( pkt.rxTm_f );
00182   pkt.txTm_s = ntohl( pkt.txTm_s );
00183   pkt.txTm_f = ntohl( pkt.txTm_f );
00184 
00185   //Compute offset, see RFC 4330 p.13
00186   uint32_t destTm_s = (NTP_TIMESTAMP_DELTA + time(NULL));
00187   int64_t offset = ( (int64_t)( pkt.rxTm_s - pkt.origTm_s ) + (int64_t) ( pkt.txTm_s - destTm_s ) ) / 2; //Avoid overflow
00188   DBG("Sent @%ul", pkt.txTm_s);
00189   DBG("Offset: %ul", offset);
00190   //Set time accordingly
00191   set_time( time(NULL) + offset );
00192 
00193 #if __DEBUG__ >= 3
00194   ctTime = time(NULL);
00195   INFO("Time is now (UTC): %s", ctime(&ctTime));
00196 #endif
00197 
00198   socket::close(sock);
00199 
00200   return OK;
00201 }
00202