NTP client to get UTC time from the internet time servers

Dependents:   NTPClient_HelloWorld

Fork of NTPClient by ST Expansion SW Team

Committer:
donatien
Date:
Fri May 25 13:48:49 2012 +0000
Revision:
0:04a82df0f587
Child:
1:b221a8765b3f
Initial Hg Commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
donatien 0:04a82df0f587 1 /* NTPClient.cpp */
donatien 0:04a82df0f587 2 /*
donatien 0:04a82df0f587 3 Copyright (C) 2012 ARM Limited.
donatien 0:04a82df0f587 4
donatien 0:04a82df0f587 5 Permission is hereby granted, free of charge, to any person obtaining a copy of
donatien 0:04a82df0f587 6 this software and associated documentation files (the "Software"), to deal in
donatien 0:04a82df0f587 7 the Software without restriction, including without limitation the rights to
donatien 0:04a82df0f587 8 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
donatien 0:04a82df0f587 9 of the Software, and to permit persons to whom the Software is furnished to do
donatien 0:04a82df0f587 10 so, subject to the following conditions:
donatien 0:04a82df0f587 11
donatien 0:04a82df0f587 12 The above copyright notice and this permission notice shall be included in all
donatien 0:04a82df0f587 13 copies or substantial portions of the Software.
donatien 0:04a82df0f587 14
donatien 0:04a82df0f587 15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
donatien 0:04a82df0f587 16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
donatien 0:04a82df0f587 17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
donatien 0:04a82df0f587 18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
donatien 0:04a82df0f587 19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
donatien 0:04a82df0f587 20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
donatien 0:04a82df0f587 21 SOFTWARE.
donatien 0:04a82df0f587 22 */
donatien 0:04a82df0f587 23
donatien 0:04a82df0f587 24 #define __DEBUG__ 4 //Maximum verbosity
donatien 0:04a82df0f587 25 #ifndef __MODULE__
donatien 0:04a82df0f587 26 #define __MODULE__ "NTPClient.cpp"
donatien 0:04a82df0f587 27 #endif
donatien 0:04a82df0f587 28
donatien 0:04a82df0f587 29 #include "core/fwk.h"
donatien 0:04a82df0f587 30
donatien 0:04a82df0f587 31 #include "NTPClient.h"
donatien 0:04a82df0f587 32
donatien 0:04a82df0f587 33 #include "mbed.h" //time() and set_time()
donatien 0:04a82df0f587 34
donatien 0:04a82df0f587 35 #define NTP_PORT 123
donatien 0:04a82df0f587 36 #define NTP_CLIENT_PORT 0 //Random port
donatien 0:04a82df0f587 37 #define NTP_REQUEST_TIMEOUT 15000
donatien 0:04a82df0f587 38 #define NTP_TIMESTAMP_DELTA 2208988800ull //Diff btw a UNIX timestamp (Starting Jan, 1st 1970) and a NTP timestamp (Starting Jan, 1st 1900)
donatien 0:04a82df0f587 39
donatien 0:04a82df0f587 40 NTPClient::NTPClient()
donatien 0:04a82df0f587 41 {
donatien 0:04a82df0f587 42
donatien 0:04a82df0f587 43
donatien 0:04a82df0f587 44 }
donatien 0:04a82df0f587 45
donatien 0:04a82df0f587 46 int NTPClient::setTime(const char* host, uint16_t port, uint32_t timeout)
donatien 0:04a82df0f587 47 {
donatien 0:04a82df0f587 48 struct sockaddr_in serverAddr;
donatien 0:04a82df0f587 49
donatien 0:04a82df0f587 50 std::memset(&serverAddr, 0, sizeof(struct sockaddr_in));
donatien 0:04a82df0f587 51
donatien 0:04a82df0f587 52 #if __DEBUG__ >= 3
donatien 0:04a82df0f587 53 time_t ctTime;
donatien 0:04a82df0f587 54 ctTime = time(NULL);
donatien 0:04a82df0f587 55 INFO("Time is set to (UTC): %s", ctime(&ctTime));
donatien 0:04a82df0f587 56 #endif
donatien 0:04a82df0f587 57
donatien 0:04a82df0f587 58 //Resolve DNS if needed
donatien 0:04a82df0f587 59 if(serverAddr.sin_addr.s_addr == 0)
donatien 0:04a82df0f587 60 {
donatien 0:04a82df0f587 61 DBG("Resolving DNS address or populate hard-coded IP address");
donatien 0:04a82df0f587 62 struct hostent *server = socket::gethostbyname(host);
donatien 0:04a82df0f587 63 if(server == NULL)
donatien 0:04a82df0f587 64 {
donatien 0:04a82df0f587 65 return NET_NOTFOUND; //Fail
donatien 0:04a82df0f587 66 }
donatien 0:04a82df0f587 67 memcpy((char*)&serverAddr.sin_addr.s_addr, (char*)server->h_addr_list[0], server->h_length);
donatien 0:04a82df0f587 68 }
donatien 0:04a82df0f587 69
donatien 0:04a82df0f587 70 serverAddr.sin_family = AF_INET;
donatien 0:04a82df0f587 71 serverAddr.sin_port = htons(port);
donatien 0:04a82df0f587 72
donatien 0:04a82df0f587 73 //Now create & bind socket
donatien 0:04a82df0f587 74 DBG("Creating socket");
donatien 0:04a82df0f587 75 int sock = socket::socket(AF_INET, SOCK_DGRAM, 0); //UDP socket
donatien 0:04a82df0f587 76 if (sock < 0)
donatien 0:04a82df0f587 77 {
donatien 0:04a82df0f587 78 ERR("Could not create socket");
donatien 0:04a82df0f587 79 return NET_OOM;
donatien 0:04a82df0f587 80 }
donatien 0:04a82df0f587 81 DBG("Handle is %d",sock);
donatien 0:04a82df0f587 82
donatien 0:04a82df0f587 83 //Create local address
donatien 0:04a82df0f587 84 struct sockaddr_in localhostAddr;
donatien 0:04a82df0f587 85
donatien 0:04a82df0f587 86 std::memset(&localhostAddr, 0, sizeof(struct sockaddr_in));
donatien 0:04a82df0f587 87
donatien 0:04a82df0f587 88 localhostAddr.sin_family = AF_INET;
donatien 0:04a82df0f587 89 localhostAddr.sin_port = htons(NTP_CLIENT_PORT); //Random port
donatien 0:04a82df0f587 90 localhostAddr.sin_addr.s_addr = htonl(INADDR_ANY); //Any local address
donatien 0:04a82df0f587 91
donatien 0:04a82df0f587 92 if ( socket::bind( sock, (struct sockaddr*)&localhostAddr, sizeof(localhostAddr)) < 0 ) //Listen on local port
donatien 0:04a82df0f587 93 {
donatien 0:04a82df0f587 94 ERR("Could not bind socket");
donatien 0:04a82df0f587 95 socket::close(sock);
donatien 0:04a82df0f587 96 return NET_OOM;
donatien 0:04a82df0f587 97 }
donatien 0:04a82df0f587 98
donatien 0:04a82df0f587 99 struct NTPPacket pkt;
donatien 0:04a82df0f587 100
donatien 0:04a82df0f587 101 //Now ping the server and wait for response
donatien 0:04a82df0f587 102 DBG("Ping");
donatien 0:04a82df0f587 103 //Prepare NTP Packet:
donatien 0:04a82df0f587 104 pkt.li = 0; //Leap Indicator : No warning
donatien 0:04a82df0f587 105 pkt.vn = 4; //Version Number : 4
donatien 0:04a82df0f587 106 pkt.mode = 3; //Client mode
donatien 0:04a82df0f587 107 pkt.stratum = 0; //Not relevant here
donatien 0:04a82df0f587 108 pkt.poll = 0; //Not significant as well
donatien 0:04a82df0f587 109 pkt.precision = 0; //Neither this one is
donatien 0:04a82df0f587 110
donatien 0:04a82df0f587 111 pkt.rootDelay = 0; //Or this one
donatien 0:04a82df0f587 112 pkt.rootDispersion = 0; //Or that one
donatien 0:04a82df0f587 113 pkt.refId = 0; //...
donatien 0:04a82df0f587 114
donatien 0:04a82df0f587 115 pkt.refTm_s = 0;
donatien 0:04a82df0f587 116 pkt.origTm_s = 0;
donatien 0:04a82df0f587 117 pkt.rxTm_s = 0;
donatien 0:04a82df0f587 118 pkt.txTm_s = htonl( NTP_TIMESTAMP_DELTA + time(NULL) ); //WARN: We are in LE format, network byte order is BE
donatien 0:04a82df0f587 119
donatien 0:04a82df0f587 120 pkt.refTm_f = pkt.origTm_f = pkt.rxTm_f = pkt.txTm_f = 0;
donatien 0:04a82df0f587 121
donatien 0:04a82df0f587 122 //Set timeout, non-blocking and wait using select
donatien 0:04a82df0f587 123 if( socket::sendto( sock, (void*)&pkt, sizeof(NTPPacket), 0, (struct sockaddr*)&serverAddr, sizeof(serverAddr) ) < 0 )
donatien 0:04a82df0f587 124 {
donatien 0:04a82df0f587 125 ERR("Could not send packet");
donatien 0:04a82df0f587 126 socket::close(sock);
donatien 0:04a82df0f587 127 return NET_CONN;
donatien 0:04a82df0f587 128 }
donatien 0:04a82df0f587 129
donatien 0:04a82df0f587 130 //Wait for socket to be readable
donatien 0:04a82df0f587 131 //Creating FS set
donatien 0:04a82df0f587 132 fd_set socksSet;
donatien 0:04a82df0f587 133 FD_ZERO(&socksSet);
donatien 0:04a82df0f587 134 FD_SET(sock, &socksSet);
donatien 0:04a82df0f587 135 struct timeval t_val;
donatien 0:04a82df0f587 136 t_val.tv_sec = timeout / 1000;
donatien 0:04a82df0f587 137 t_val.tv_usec = (timeout - (t_val.tv_sec * 1000)) * 1000;
donatien 0:04a82df0f587 138 int ret = socket::select(FD_SETSIZE, &socksSet, NULL, NULL, &t_val);
donatien 0:04a82df0f587 139 if(ret <= 0 || !FD_ISSET(sock, &socksSet))
donatien 0:04a82df0f587 140 {
donatien 0:04a82df0f587 141 ERR("Timeout while waiting for answer");
donatien 0:04a82df0f587 142 socket::close(sock);
donatien 0:04a82df0f587 143 return NET_TIMEOUT; //Timeout
donatien 0:04a82df0f587 144 }
donatien 0:04a82df0f587 145
donatien 0:04a82df0f587 146 //Read response
donatien 0:04a82df0f587 147 DBG("Pong");
donatien 0:04a82df0f587 148 struct sockaddr_in respAddr;
donatien 0:04a82df0f587 149 socklen_t respAddrLen = sizeof(respAddr);
donatien 0:04a82df0f587 150 do
donatien 0:04a82df0f587 151 {
donatien 0:04a82df0f587 152 ret = socket::recvfrom( sock, (void*)&pkt, sizeof(NTPPacket), 0, (struct sockaddr*)&respAddr, &respAddrLen);
donatien 0:04a82df0f587 153 if(ret < 0)
donatien 0:04a82df0f587 154 {
donatien 0:04a82df0f587 155 ERR("Could not receive packet");
donatien 0:04a82df0f587 156 socket::close(sock);
donatien 0:04a82df0f587 157 return NET_CONN;
donatien 0:04a82df0f587 158 }
donatien 0:04a82df0f587 159 } while( respAddr.sin_addr.s_addr != serverAddr.sin_addr.s_addr);
donatien 0:04a82df0f587 160
donatien 0:04a82df0f587 161 if(ret < sizeof(NTPPacket)) //TODO: Accept chunks
donatien 0:04a82df0f587 162 {
donatien 0:04a82df0f587 163 ERR("Receive packet size does not match");
donatien 0:04a82df0f587 164 socket::close(sock);
donatien 0:04a82df0f587 165 return NET_PROTOCOL;
donatien 0:04a82df0f587 166 }
donatien 0:04a82df0f587 167
donatien 0:04a82df0f587 168 if( pkt.stratum == 0) //Kiss of death message : Not good !
donatien 0:04a82df0f587 169 {
donatien 0:04a82df0f587 170 ERR("Kissed to death!");
donatien 0:04a82df0f587 171 socket::close(sock);
donatien 0:04a82df0f587 172 return NTP_PORT;
donatien 0:04a82df0f587 173 }
donatien 0:04a82df0f587 174
donatien 0:04a82df0f587 175 //Correct Endianness
donatien 0:04a82df0f587 176 pkt.refTm_s = ntohl( pkt.refTm_s );
donatien 0:04a82df0f587 177 pkt.refTm_f = ntohl( pkt.refTm_f );
donatien 0:04a82df0f587 178 pkt.origTm_s = ntohl( pkt.origTm_s );
donatien 0:04a82df0f587 179 pkt.origTm_f = ntohl( pkt.origTm_f );
donatien 0:04a82df0f587 180 pkt.rxTm_s = ntohl( pkt.rxTm_s );
donatien 0:04a82df0f587 181 pkt.rxTm_f = ntohl( pkt.rxTm_f );
donatien 0:04a82df0f587 182 pkt.txTm_s = ntohl( pkt.txTm_s );
donatien 0:04a82df0f587 183 pkt.txTm_f = ntohl( pkt.txTm_f );
donatien 0:04a82df0f587 184
donatien 0:04a82df0f587 185 //Compute offset, see RFC 4330 p.13
donatien 0:04a82df0f587 186 uint32_t destTm_s = (NTP_TIMESTAMP_DELTA + time(NULL));
donatien 0:04a82df0f587 187 int64_t offset = ( (int64_t)( pkt.rxTm_s - pkt.origTm_s ) + (int64_t) ( pkt.txTm_s - destTm_s ) ) / 2; //Avoid overflow
donatien 0:04a82df0f587 188 DBG("Sent @%ul", pkt.txTm_s);
donatien 0:04a82df0f587 189 DBG("Offset: %ul", offset);
donatien 0:04a82df0f587 190 //Set time accordingly
donatien 0:04a82df0f587 191 set_time( time(NULL) + offset );
donatien 0:04a82df0f587 192
donatien 0:04a82df0f587 193 #if __DEBUG__ >= 3
donatien 0:04a82df0f587 194 ctTime = time(NULL);
donatien 0:04a82df0f587 195 INFO("Time is now (UTC): %s", ctime(&ctTime));
donatien 0:04a82df0f587 196 #endif
donatien 0:04a82df0f587 197
donatien 0:04a82df0f587 198 socket::close(sock);
donatien 0:04a82df0f587 199
donatien 0:04a82df0f587 200 return OK;
donatien 0:04a82df0f587 201 }
donatien 0:04a82df0f587 202