Committer:
donatien
Date:
Thu May 31 16:02:20 2012 +0000
Revision:
0:9fa20c138e48

        

Who changed what in which revision?

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