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