synctime.c (12382B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 /* <DESC> 25 * Set your system time from a remote HTTP server's Date: header. 26 * </DESC> 27 */ 28 /* This example code only builds as-is on Windows. 29 * 30 * Synchronising your computer clock via Internet time server usually relies 31 * on DAYTIME, TIME, or NTP protocols. These protocols provide good accurate 32 * time synchronization but it does not work well through a firewall/proxy. 33 * Some adjustment has to be made to the firewall/proxy for these protocols to 34 * work properly. 35 * 36 * There is an indirect method. Since most webserver provide server time in 37 * their HTTP header, therefore you could synchronise your computer clock 38 * using HTTP protocol which has no problem with firewall/proxy. 39 * 40 * For this software to work, you should take note of these items. 41 * 1. Your firewall/proxy must allow your computer to surf Internet. 42 * 2. Webserver system time must in sync with the NTP time server, 43 * or at least provide an accurate time keeping. 44 * 3. Webserver HTTP header does not provide the milliseconds units, 45 * so there is no way to get an accurate time. 46 * 4. This software could only provide an accuracy of +- a few seconds, 47 * as Round-Trip delay time is not taken into consideration. 48 * Compensation of network, firewall/proxy delay cannot be simply divide 49 * the Round-Trip delay time by half. 50 * 5. Win32 SetSystemTime() API sets your computer clock according to 51 * GMT/UTC time. Therefore your computer timezone must be properly set. 52 * 6. Webserver data should not be cached by the proxy server. Some 53 * webserver provide Cache-Control to prevent caching. 54 * 55 * Usage: 56 * This software synchronises your computer clock only when you issue 57 * it with --synctime. By default, it only display the webserver's clock. 58 * 59 * Written by: Frank (contributed to libcurl) 60 * 61 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 62 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 63 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 64 * 65 * IN NO EVENT SHALL THE AUTHOR OF THIS SOFTWARE BE LIABLE FOR 66 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, 67 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 68 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 69 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 70 * OF THIS SOFTWARE. 71 * 72 */ 73 74 #include <stdio.h> 75 #include <time.h> 76 #include <curl/curl.h> 77 78 #ifdef _WIN32 79 #include <windows.h> 80 #else 81 #error "This example requires Windows." 82 #endif 83 84 85 #define MAX_STRING 256 86 #define MAX_STRING1 MAX_STRING + 1 87 88 #define SYNCTIME_UA "synctime/1.0" 89 90 struct conf { 91 char http_proxy[MAX_STRING1]; 92 char proxy_user[MAX_STRING1]; 93 char timeserver[MAX_STRING1]; 94 }; 95 96 static const char DefaultTimeServer[3][MAX_STRING1] = 97 { 98 "https://nist.time.gov/", 99 "https://www.google.com/" 100 }; 101 102 static const char *DayStr[] = { 103 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; 104 static const char *MthStr[] = { 105 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 106 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; 107 108 static int ShowAllHeader; 109 static int AutoSyncTime; 110 static SYSTEMTIME SYSTime; 111 static SYSTEMTIME LOCALTime; 112 113 #define HTTP_COMMAND_HEAD 0 114 #define HTTP_COMMAND_GET 1 115 116 117 static size_t SyncTime_CURL_WriteOutput(void *ptr, size_t size, size_t nmemb, 118 void *stream) 119 { 120 fwrite(ptr, size, nmemb, stream); 121 return nmemb * size; 122 } 123 124 static size_t SyncTime_CURL_WriteHeader(void *ptr, size_t size, size_t nmemb, 125 void *stream) 126 { 127 char TmpStr1[26], TmpStr2[26]; 128 129 (void)stream; 130 131 if(ShowAllHeader == 1) 132 fprintf(stderr, "%s", (char *)(ptr)); 133 134 if(strncmp((char *)(ptr), "Date:", 5) == 0) { 135 if(ShowAllHeader == 0) 136 fprintf(stderr, "HTTP Server. %s", (char *)(ptr)); 137 138 if(AutoSyncTime == 1) { 139 *TmpStr1 = 0; 140 *TmpStr2 = 0; 141 if(strlen((char *)(ptr)) > 50) /* Can prevent buffer overflow to 142 TmpStr1 & 2? */ 143 AutoSyncTime = 0; 144 else { 145 int RetVal = sscanf((char *)(ptr), "Date: %25s %hu %s %hu %hu:%hu:%hu", 146 TmpStr1, &SYSTime.wDay, TmpStr2, &SYSTime.wYear, 147 &SYSTime.wHour, &SYSTime.wMinute, 148 &SYSTime.wSecond); 149 150 if(RetVal == 7) { 151 int i; 152 SYSTime.wMilliseconds = 500; /* adjust to midpoint, 0.5 sec */ 153 for(i = 0; i < 12; i++) { 154 if(strcmp(MthStr[i], TmpStr2) == 0) { 155 SYSTime.wMonth = (WORD)(i + 1); 156 break; 157 } 158 } 159 AutoSyncTime = 3; /* Computer clock is adjusted */ 160 } 161 else { 162 AutoSyncTime = 0; /* Error in sscanf() fields conversion */ 163 } 164 } 165 } 166 } 167 168 if(strncmp((char *)(ptr), "X-Cache: HIT", 12) == 0) { 169 fprintf(stderr, "ERROR: HTTP Server data is cached." 170 " Server Date is no longer valid.\n"); 171 AutoSyncTime = 0; 172 } 173 return nmemb * size; 174 } 175 176 static void SyncTime_CURL_Init(CURL *curl, const char *proxy_port, 177 const char *proxy_user_password) 178 { 179 if(strlen(proxy_port) > 0) 180 curl_easy_setopt(curl, CURLOPT_PROXY, proxy_port); 181 182 if(strlen(proxy_user_password) > 0) 183 curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_user_password); 184 185 curl_easy_setopt(curl, CURLOPT_USERAGENT, SYNCTIME_UA); 186 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, SyncTime_CURL_WriteOutput); 187 curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, SyncTime_CURL_WriteHeader); 188 } 189 190 static CURLcode SyncTime_CURL_Fetch(CURL *curl, const char *URL_Str, 191 const char *OutFileName, int HttpGetBody) 192 { 193 FILE *outfile; 194 CURLcode res; 195 196 outfile = NULL; 197 if(HttpGetBody == HTTP_COMMAND_HEAD) 198 curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); 199 else { 200 outfile = fopen(OutFileName, "wb"); 201 curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile); 202 } 203 204 curl_easy_setopt(curl, CURLOPT_URL, URL_Str); 205 res = curl_easy_perform(curl); 206 if(outfile) 207 fclose(outfile); 208 return res; /* (CURLE_OK) */ 209 } 210 211 static void showUsage(void) 212 { 213 fprintf(stderr, "synctime: Synchronising computer clock with time server" 214 " using HTTP protocol.\n"); 215 fprintf(stderr, "Usage : synctime [Option]\n"); 216 fprintf(stderr, "Options :\n"); 217 fprintf(stderr, " --server=WEBSERVER Use this time server instead" 218 " of default.\n"); 219 fprintf(stderr, " --showall Show all HTTP header.\n"); 220 fprintf(stderr, " --synctime Synchronising computer clock" 221 " with time server.\n"); 222 fprintf(stderr, " --proxy-user=USER[:PASS] Set proxy username and" 223 " password.\n"); 224 fprintf(stderr, " --proxy=HOST[:PORT] Use HTTP proxy on given" 225 " port.\n"); 226 fprintf(stderr, " --help Print this help.\n"); 227 fprintf(stderr, "\n"); 228 return; 229 } 230 231 static int conf_init(struct conf *conf) 232 { 233 int i; 234 235 *conf->http_proxy = 0; 236 for(i = 0; i < MAX_STRING1; i++) 237 conf->proxy_user[i] = 0; /* Clean up password from memory */ 238 *conf->timeserver = 0; 239 return 1; 240 } 241 242 int main(int argc, char *argv[]) 243 { 244 CURL *curl; 245 struct conf conf[1]; 246 int RetValue; 247 248 ShowAllHeader = 0; /* Do not show HTTP Header */ 249 AutoSyncTime = 0; /* Do not synchronise computer clock */ 250 RetValue = 0; /* Successful Exit */ 251 conf_init(conf); 252 253 if(argc > 1) { 254 int OptionIndex = 0; 255 while(OptionIndex < argc) { 256 if(strncmp(argv[OptionIndex], "--server=", 9) == 0) 257 snprintf(conf->timeserver, MAX_STRING, "%s", &argv[OptionIndex][9]); 258 259 if(strcmp(argv[OptionIndex], "--showall") == 0) 260 ShowAllHeader = 1; 261 262 if(strcmp(argv[OptionIndex], "--synctime") == 0) 263 AutoSyncTime = 1; 264 265 if(strncmp(argv[OptionIndex], "--proxy-user=", 13) == 0) 266 snprintf(conf->proxy_user, MAX_STRING, "%s", &argv[OptionIndex][13]); 267 268 if(strncmp(argv[OptionIndex], "--proxy=", 8) == 0) 269 snprintf(conf->http_proxy, MAX_STRING, "%s", &argv[OptionIndex][8]); 270 271 if((strcmp(argv[OptionIndex], "--help") == 0) || 272 (strcmp(argv[OptionIndex], "/?") == 0)) { 273 showUsage(); 274 return 0; 275 } 276 OptionIndex++; 277 } 278 } 279 280 if(*conf->timeserver == 0) /* Use default server for time information */ 281 snprintf(conf->timeserver, MAX_STRING, "%s", DefaultTimeServer[0]); 282 283 /* Init CURL before usage */ 284 curl_global_init(CURL_GLOBAL_ALL); 285 curl = curl_easy_init(); 286 if(curl) { 287 struct tm *lt; 288 struct tm *gmt; 289 time_t tt; 290 time_t tt_local; 291 time_t tt_gmt; 292 double tzonediffFloat; 293 int tzonediffWord; 294 char timeBuf[61]; 295 char tzoneBuf[16]; 296 297 SyncTime_CURL_Init(curl, conf->http_proxy, conf->proxy_user); 298 299 /* Calculating time diff between GMT and localtime */ 300 tt = time(0); 301 /* !checksrc! disable BANNEDFUNC 1 */ 302 lt = localtime(&tt); 303 tt_local = mktime(lt); 304 /* !checksrc! disable BANNEDFUNC 1 */ 305 gmt = gmtime(&tt); 306 tt_gmt = mktime(gmt); 307 tzonediffFloat = difftime(tt_local, tt_gmt); 308 tzonediffWord = (int)(tzonediffFloat/3600.0); 309 310 if((double)(tzonediffWord * 3600) == tzonediffFloat) 311 snprintf(tzoneBuf, sizeof(tzoneBuf), "%+03d'00'", tzonediffWord); 312 else 313 snprintf(tzoneBuf, sizeof(tzoneBuf), "%+03d'30'", tzonediffWord); 314 315 /* Get current system time and local time */ 316 GetSystemTime(&SYSTime); 317 GetLocalTime(&LOCALTime); 318 snprintf(timeBuf, 60, "%s, %02d %s %04d %02d:%02d:%02d.%03d, ", 319 DayStr[LOCALTime.wDayOfWeek], LOCALTime.wDay, 320 MthStr[LOCALTime.wMonth-1], LOCALTime.wYear, 321 LOCALTime.wHour, LOCALTime.wMinute, LOCALTime.wSecond, 322 LOCALTime.wMilliseconds); 323 324 fprintf(stderr, "Fetch: %s\n\n", conf->timeserver); 325 fprintf(stderr, "Before HTTP. Date: %s%s\n\n", timeBuf, tzoneBuf); 326 327 /* HTTP HEAD command to the Webserver */ 328 SyncTime_CURL_Fetch(curl, conf->timeserver, "index.htm", 329 HTTP_COMMAND_HEAD); 330 331 GetLocalTime(&LOCALTime); 332 snprintf(timeBuf, 60, "%s, %02d %s %04d %02d:%02d:%02d.%03d, ", 333 DayStr[LOCALTime.wDayOfWeek], LOCALTime.wDay, 334 MthStr[LOCALTime.wMonth-1], LOCALTime.wYear, 335 LOCALTime.wHour, LOCALTime.wMinute, LOCALTime.wSecond, 336 LOCALTime.wMilliseconds); 337 fprintf(stderr, "\nAfter HTTP. Date: %s%s\n", timeBuf, tzoneBuf); 338 339 if(AutoSyncTime == 3) { 340 /* Synchronising computer clock */ 341 if(!SetSystemTime(&SYSTime)) { /* Set system time */ 342 fprintf(stderr, "ERROR: Unable to set system time.\n"); 343 RetValue = 1; 344 } 345 else { 346 /* Successfully re-adjusted computer clock */ 347 GetLocalTime(&LOCALTime); 348 snprintf(timeBuf, 60, "%s, %02d %s %04d %02d:%02d:%02d.%03d, ", 349 DayStr[LOCALTime.wDayOfWeek], LOCALTime.wDay, 350 MthStr[LOCALTime.wMonth-1], LOCALTime.wYear, 351 LOCALTime.wHour, LOCALTime.wMinute, LOCALTime.wSecond, 352 LOCALTime.wMilliseconds); 353 fprintf(stderr, "\nNew System's Date: %s%s\n", timeBuf, tzoneBuf); 354 } 355 } 356 357 /* Cleanup before exit */ 358 conf_init(conf); 359 curl_easy_cleanup(curl); 360 } 361 return RetValue; 362 }