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