1 /***************************************************************************** 2 * 3 * libntpq.c 4 * 5 * This is the wrapper library for ntpq, the NTP query utility. 6 * This library reuses the sourcecode from ntpq and exports a number 7 * of useful functions in a library that can be linked against applications 8 * that need to query the status of a running ntpd. The whole 9 * communcation is based on mode 6 packets. 10 * 11 ****************************************************************************/ 12 #define LIBNTPQ_C 13 #define NO_MAIN_ALLOWED 1 14 /* #define BUILD_AS_LIB Already provided by the Makefile */ 15 16 #include "ntpq.c" 17 #include "libntpq.h" 18 19 /* Function Prototypes */ 20 21 22 const char *Version = "libntpq 0.3beta"; 23 24 /* global variables used for holding snapshots of data */ 25 char peervars[NTPQ_BUFLEN]; 26 int peervarlen = 0; 27 associd_t peervar_assoc = 0; 28 char clockvars[NTPQ_BUFLEN]; 29 int clockvarlen = 0; 30 int clockvar_assoc = 0; 31 char sysvars[NTPQ_BUFLEN]; 32 int sysvarlen = 0; 33 char *ntpq_resultbuffer[NTPQ_BUFLEN]; 34 unsigned short ntpq_associations[MAXASSOC]; 35 struct ntpq_varlist ntpq_varlist[MAXLIST]; 36 37 /***************************************************************************** 38 * 39 * ntpq_stripquotes 40 * 41 * Parses a given character buffer srcbuf and removes all quoted 42 * characters. The resulting string is copied to the specified 43 * resultbuf character buffer. E.g. \" will be translated into " 44 * 45 **************************************************************************** 46 * Parameters: 47 * resultbuf char* The resulting string without quoted 48 * characters 49 * srcbuf char* The buffer holding the original string 50 * datalen int The number of bytes stored in srcbuf 51 * maxlen int Max. number of bytes for resultbuf 52 * 53 * Returns: 54 * int number of chars that have been copied to 55 * resultbuf 56 ****************************************************************************/ 57 58 int ntpq_stripquotes ( char *resultbuf, char *srcbuf, int datalen, int maxlen ) 59 { 60 char* dst = resultbuf; 61 char* dep = resultbuf + maxlen - 1; 62 char* src = srcbuf; 63 char* sep = srcbuf + (datalen >= 0 ? datalen : 0); 64 int esc = 0; 65 int ch; 66 67 if (maxlen <= 0) 68 return 0; 69 70 while ((dst != dep) && (src != sep) && (ch = (u_char)*src++) != 0) { 71 if (esc) { 72 esc = 0; 73 switch (ch) { 74 /* skip and do not copy */ 75 /* case '"':*/ /* quotes */ 76 case 'n': /*newline*/ 77 case 'r': /*carriage return*/ 78 case 'g': /*bell*/ 79 case 't': /*tab*/ 80 continue; 81 default: 82 break; 83 } 84 } else { 85 switch (ch) { 86 case '\\': 87 esc = 1; 88 case '"': 89 continue; 90 default: 91 break; 92 } 93 } 94 *dst++ = (char)ch; 95 } 96 *dst = '\0'; 97 return (int)(dst - resultbuf); 98 } 99 100 101 /***************************************************************************** 102 * 103 * ntpq_getvar 104 * 105 * This function parses a given buffer for a variable/value pair and 106 * copies the value of the requested variable into the specified 107 * varvalue buffer. 108 * 109 * It returns the number of bytes copied or zero for an empty result 110 * (=no matching variable found or empty value) 111 * 112 **************************************************************************** 113 * Parameters: 114 * resultbuf char* The resulting string without quoted 115 * characters 116 * datalen size_t The number of bytes stored in 117 * resultbuf 118 * varname char* Name of the required variable 119 * varvalue char* Where the value of the variable should 120 * be stored 121 * maxlen size_t Max. number of bytes for varvalue 122 * 123 * Returns: 124 * size_t number of chars that have been copied to 125 * varvalue 126 ****************************************************************************/ 127 128 size_t 129 ntpq_getvar( 130 const char * resultbuf, 131 size_t datalen, 132 const char * varname, 133 char * varvalue, 134 size_t maxlen) 135 { 136 char * name; 137 char * value; 138 size_t idatalen; 139 140 value = NULL; 141 idatalen = (int)datalen; 142 143 while (nextvar(&idatalen, &resultbuf, &name, &value)) { 144 if (strcmp(varname, name) == 0) { 145 ntpq_stripquotes(varvalue, value, strlen(value), maxlen); 146 147 return strlen(varvalue); 148 } 149 } 150 151 return 0; 152 } 153 154 155 /***************************************************************************** 156 * 157 * ntpq_queryhost 158 * 159 * Sends a mode 6 query packet to the current open host (see 160 * ntpq_openhost) and stores the requested variable set in the specified 161 * character buffer. 162 * It returns the number of bytes read or zero for an empty result 163 * (=no answer or empty value) 164 * 165 **************************************************************************** 166 * Parameters: 167 * VARSET u_short Which variable set should be 168 * read (PEERVARS or CLOCKVARS) 169 * association int The association ID that should be read 170 * 0 represents the ntpd instance itself 171 * resultbuf char* The resulting string without quoted 172 * characters 173 * maxlen int Max. number of bytes for varvalue 174 * 175 * Returns: 176 * int number of bytes that have been copied to 177 * resultbuf 178 * - OR - 179 * 0 (zero) if no reply has been received or 180 * another failure occured 181 ****************************************************************************/ 182 183 int ntpq_queryhost(unsigned short VARSET, unsigned short association, char *resultbuf, int maxlen) 184 { 185 const char *datap; 186 int res; 187 size_t dsize; 188 u_short rstatus; 189 190 if ( numhosts > 0 ) 191 res = doquery(VARSET,association,0,0, (char *)0, &rstatus, &dsize, &datap); 192 else 193 return 0; 194 195 if ( ( res != 0) || ( dsize == 0 ) ) /* no data */ 196 return 0; 197 198 if ( dsize > maxlen) 199 dsize = maxlen; 200 201 202 /* fill result resultbuf */ 203 memcpy(resultbuf, datap, dsize); 204 205 return dsize; 206 } 207 208 209 210 /***************************************************************************** 211 * 212 * ntpq_openhost 213 * 214 * Sets up a connection to the ntpd instance of a specified host. Note: 215 * There is no real "connection" established because NTP solely works 216 * based on UDP. 217 * 218 **************************************************************************** 219 * Parameters: 220 * hostname char* Hostname/IP of the host running ntpd 221 * fam int Address Family (AF_INET, AF_INET6, or 0) 222 * 223 * Returns: 224 * int 1 if the host connection could be set up, i.e. 225 * name resolution was succesful and/or IP address 226 * has been validated 227 * - OR - 228 * 0 (zero) if a failure occured 229 ****************************************************************************/ 230 231 int 232 ntpq_openhost( 233 char *hostname, 234 int fam 235 ) 236 { 237 if ( openhost(hostname, fam) ) 238 { 239 numhosts = 1; 240 } else { 241 numhosts = 0; 242 } 243 244 return numhosts; 245 246 } 247 248 249 /***************************************************************************** 250 * 251 * ntpq_closehost 252 * 253 * Cleans up a connection by closing the used socket. Should be called 254 * when no further queries are required for the currently used host. 255 * 256 **************************************************************************** 257 * Parameters: 258 * - none - 259 * 260 * Returns: 261 * int 0 (zero) if no host has been opened before 262 * - OR - 263 * the resultcode from the closesocket function call 264 ****************************************************************************/ 265 266 int ntpq_closehost(void) 267 { 268 if ( numhosts ) 269 return closesocket(sockfd); 270 271 return 0; 272 } 273 274 275 /***************************************************************************** 276 * 277 * ntpq_read_associations 278 * 279 * This function queries the ntp host for its associations and returns the 280 * number of associations found. 281 * 282 * It takes an u_short array as its first parameter, this array holds the 283 * IDs of the associations, 284 * the function will not write more entries than specified with the 285 * max_entries parameter. 286 * 287 * However, if more than max_entries associations were found, the return 288 * value of this function will reflect the real number, even if not all 289 * associations have been stored in the array. 290 * 291 **************************************************************************** 292 * Parameters: 293 * resultbuf u_short*Array that should hold the list of 294 * association IDs 295 * maxentries int maximum number of association IDs that can 296 * be stored in resultbuf 297 * 298 * Returns: 299 * int number of association IDs stored in resultbuf 300 * - OR - 301 * 0 (zero) if a failure occured or no association has 302 * been returned. 303 ****************************************************************************/ 304 305 int ntpq_read_associations ( u_short resultbuf[], int max_entries ) 306 { 307 int i = 0; 308 309 if (ntpq_dogetassoc()) { 310 311 if(numassoc < max_entries) 312 max_entries = numassoc; 313 314 for (i=0;i<max_entries;i++) 315 resultbuf[i] = assoc_cache[i].assid; 316 317 return numassoc; 318 } 319 320 return 0; 321 } 322 323 324 325 326 /***************************************************************************** 327 * 328 * ntpq_get_assocs 329 * 330 * This function reads the associations of a previously selected (with 331 * ntpq_openhost) NTP host into its own (global) array and returns the 332 * number of associations found. 333 * 334 * The obtained association IDs can be read by using the ntpq_get_assoc_id 335 * function. 336 * 337 **************************************************************************** 338 * Parameters: 339 * - none - 340 * 341 * Returns: 342 * int number of association IDs stored in resultbuf 343 * - OR - 344 * 0 (zero) if a failure occured or no association has 345 * been returned. 346 ****************************************************************************/ 347 348 int ntpq_get_assocs ( void ) 349 { 350 return ntpq_read_associations( ntpq_associations, MAXASSOC ); 351 } 352 353 354 /***************************************************************************** 355 * 356 * ntpq_get_assoc_number 357 * 358 * This function returns for a given Association ID the association number 359 * in the internal association array, which is filled by the ntpq_get_assocs 360 * function. 361 * 362 **************************************************************************** 363 * Parameters: 364 * associd int requested associaton ID 365 * 366 * Returns: 367 * int the number of the association array element that is 368 * representing the given association ID 369 * - OR - 370 * -1 if a failure occured or no matching association 371 * ID has been found 372 ****************************************************************************/ 373 374 int ntpq_get_assoc_number ( associd_t associd ) 375 { 376 int i; 377 378 for (i=0;i<numassoc;i++) { 379 if (assoc_cache[i].assid == associd) 380 return i; 381 } 382 383 return -1; 384 385 } 386 387 388 /***************************************************************************** 389 * 390 * ntpq_read_assoc_peervars 391 * 392 * This function reads the peervars variable-set of a specified association 393 * from a NTP host and writes it to the result buffer specified, honoring 394 * the maxsize limit. 395 * 396 * It returns the number of bytes written or 0 when the variable-set is 397 * empty or failed to read. 398 * 399 **************************************************************************** 400 * Parameters: 401 * associd int requested associaton ID 402 * resultbuf char* character buffer where the variable set 403 * should be stored 404 * maxsize int the maximum number of bytes that can be 405 * written to resultbuf 406 * 407 * Returns: 408 * int number of chars that have been copied to 409 * resultbuf 410 * - OR - 411 * 0 (zero) if an error occured 412 ****************************************************************************/ 413 414 int 415 ntpq_read_assoc_peervars( 416 associd_t associd, 417 char * resultbuf, 418 int maxsize 419 ) 420 { 421 const char * datap; 422 int res; 423 size_t dsize; 424 u_short rstatus; 425 426 res = doquery(CTL_OP_READVAR, associd, 0, 0, NULL, &rstatus, 427 &dsize, &datap); 428 if (res != 0) 429 return 0; 430 if (dsize <= 0) { 431 if (numhosts > 1) 432 fprintf(stderr, "server=%s ", currenthost); 433 fprintf(stderr, 434 "***No information returned for association %d\n", 435 associd); 436 437 return 0; 438 } 439 if (dsize > maxsize) 440 dsize = maxsize; 441 memcpy(resultbuf, datap, dsize); 442 443 return dsize; 444 } 445 446 447 448 449 /***************************************************************************** 450 * 451 * ntpq_read_sysvars 452 * 453 * This function reads the sysvars variable-set from a NTP host and writes it 454 * to the result buffer specified, honoring the maxsize limit. 455 * 456 * It returns the number of bytes written or 0 when the variable-set is empty 457 * or could not be read. 458 * 459 **************************************************************************** 460 * Parameters: 461 * resultbuf char* character buffer where the variable set 462 * should be stored 463 * maxsize int the maximum number of bytes that can be 464 * written to resultbuf 465 * 466 * Returns: 467 * int number of chars that have been copied to 468 * resultbuf 469 * - OR - 470 * 0 (zero) if an error occured 471 ****************************************************************************/ 472 size_t 473 ntpq_read_sysvars( 474 char * resultbuf, 475 size_t maxsize 476 ) 477 { 478 const char * datap; 479 int res; 480 size_t dsize; 481 u_short rstatus; 482 483 res = doquery(CTL_OP_READVAR, 0, 0, 0, NULL, &rstatus, 484 &dsize, &datap); 485 486 if (res != 0) 487 return 0; 488 489 if (dsize == 0) { 490 if (numhosts > 1) 491 fprintf(stderr, "server=%s ", currenthost); 492 fprintf(stderr, "***No sysvar information returned\n"); 493 494 return 0; 495 } else { 496 dsize = min(dsize, maxsize); 497 memcpy(resultbuf, datap, dsize); 498 } 499 500 return dsize; 501 } 502 503 504 /***************************************************************************** 505 * ntpq_get_assoc_allvars 506 * 507 * With this function all association variables for the specified association 508 * ID can be requested from a NTP host. They are stored internally and can be 509 * read by using the ntpq_get_peervar or ntpq_get_clockvar functions. 510 * 511 * Basically this is only a combination of the ntpq_get_assoc_peervars and 512 * ntpq_get_assoc_clockvars functions. 513 * 514 * It returns 1 if both variable-sets (peervars and clockvars) were 515 * received successfully. If one variable-set or both of them weren't 516 * received, 517 * 518 **************************************************************************** 519 * Parameters: 520 * associd int requested associaton ID 521 * 522 * Returns: 523 * int nonzero if at least one variable set could be read 524 * - OR - 525 * 0 (zero) if an error occured and both variable sets 526 * could not be read 527 ****************************************************************************/ 528 int ntpq_get_assoc_allvars( associd_t associd ) 529 { 530 return ntpq_get_assoc_peervars ( associd ) & 531 ntpq_get_assoc_clockvars( associd ); 532 } 533 534 535 536 537 /***************************************************************************** 538 * 539 * ntpq_get_sysvars 540 * 541 * The system variables of a NTP host can be requested by using this function 542 * and afterwards using ntpq_get_sysvar to read the single variable values. 543 * 544 **************************************************************************** 545 * Parameters: 546 * - none - 547 * 548 * Returns: 549 * int nonzero if the variable set could be read 550 * - OR - 551 * 0 (zero) if an error occured and the sysvars 552 * could not be read 553 ****************************************************************************/ 554 int 555 ntpq_get_sysvars(void) 556 { 557 sysvarlen = ntpq_read_sysvars(sysvars, sizeof(sysvars)); 558 if (sysvarlen <= 0) 559 return 0; 560 else 561 return 1; 562 } 563 564 565 /***************************************************************************** 566 * 567 * ntp_get_peervar 568 * 569 * This function uses the variable-set which was read by using 570 * ntp_get_peervars and searches for a variable specified with varname. If 571 * such a variable exists, it writes its value into 572 * varvalue (maxlen specifies the size of this target buffer). 573 * 574 **************************************************************************** 575 * Parameters: 576 * varname char* requested variable name 577 * varvalue char* the buffer where the value should go into 578 * maxlen int maximum number of bytes that can be copied to 579 * varvalue 580 * 581 * Returns: 582 * int number of bytes copied to varvalue 583 * - OR - 584 * 0 (zero) if an error occured or the variable could 585 * not be found 586 ****************************************************************************/ 587 int ntpq_get_peervar( const char *varname, char *varvalue, int maxlen) 588 { 589 return ( ntpq_getvar(peervars,peervarlen,varname,varvalue,maxlen) ); 590 } 591 592 593 594 /***************************************************************************** 595 * 596 * ntpq_get_assoc_peervars 597 * 598 * This function requests the peer variables of the specified association 599 * from a NTP host. In order to access the variable values, the function 600 * ntpq_get_peervar must be used. 601 * 602 **************************************************************************** 603 * Parameters: 604 * associd int requested associaton ID 605 * 606 * Returns: 607 * int 1 (one) if the peervars have been read 608 * - OR - 609 * 0 (zero) if an error occured and the variable set 610 * could not be read 611 ****************************************************************************/ 612 int 613 ntpq_get_assoc_peervars( 614 associd_t associd 615 ) 616 { 617 peervarlen = ntpq_read_assoc_peervars(associd, peervars, 618 sizeof(peervars)); 619 if (peervarlen <= 0) { 620 peervar_assoc = 0; 621 622 return 0; 623 } 624 peervar_assoc = associd; 625 626 return 1; 627 } 628 629 630 /***************************************************************************** 631 * 632 * ntp_read_assoc_clockvars 633 * 634 * This function reads the clockvars variable-set of a specified association 635 * from a NTP host and writes it to the result buffer specified, honoring 636 * the maxsize limit. 637 * 638 * It returns the number of bytes written or 0 when the variable-set is 639 * empty or failed to read. 640 * 641 **************************************************************************** 642 * Parameters: 643 * associd int requested associaton ID 644 * resultbuf char* character buffer where the variable set 645 * should be stored 646 * maxsize int the maximum number of bytes that can be 647 * written to resultbuf 648 * 649 * Returns: 650 * int number of chars that have been copied to 651 * resultbuf 652 * - OR - 653 * 0 (zero) if an error occured 654 ****************************************************************************/ 655 656 int 657 ntpq_read_assoc_clockvars( 658 associd_t associd, 659 char * resultbuf, 660 int maxsize 661 ) 662 { 663 const char *datap; 664 int res; 665 size_t dsize; 666 u_short rstatus; 667 668 res = ntpq_doquerylist(ntpq_varlist, CTL_OP_READCLOCK, associd, 669 0, &rstatus, &dsize, &datap); 670 if (res != 0) 671 return 0; 672 673 if (dsize == 0) { 674 if (numhosts > 1) /* no information returned from server */ 675 return 0; 676 } else { 677 if (dsize > maxsize) 678 dsize = maxsize; 679 memcpy(resultbuf, datap, dsize); 680 } 681 682 return dsize; 683 } 684 685 686 687 /***************************************************************************** 688 * 689 * ntpq_get_assoc_clocktype 690 * 691 * This function returns a clocktype value for a given association number 692 * (not ID!): 693 * 694 * NTP_CLOCKTYPE_UNKNOWN Unknown clock type 695 * NTP_CLOCKTYPE_BROADCAST Broadcast server 696 * NTP_CLOCKTYPE_LOCAL Local clock 697 * NTP_CLOCKTYPE_UNICAST Unicast server 698 * NTP_CLOCKTYPE_MULTICAST Multicast server 699 * 700 ****************************************************************************/ 701 int 702 ntpq_get_assoc_clocktype( 703 int assoc_index 704 ) 705 { 706 associd_t associd; 707 int i; 708 int rc; 709 sockaddr_u dum_store; 710 char dstadr[LENHOSTNAME]; 711 char resultbuf[NTPQ_BUFLEN]; 712 713 if (assoc_index < 0 || assoc_index >= numassoc) 714 return -1; 715 716 associd = assoc_cache[assoc_index].assid; 717 if (associd == peervar_assoc) { 718 rc = ntpq_get_peervar("dstadr", dstadr, sizeof(dstadr)); 719 } else { 720 i = ntpq_read_assoc_peervars(associd, resultbuf, 721 sizeof(resultbuf)); 722 if (i <= 0) 723 return -1; 724 rc = ntpq_getvar(resultbuf, i, "dstadr", dstadr, 725 sizeof(dstadr)); 726 } 727 728 if (0 != rc && decodenetnum(dstadr, &dum_store)) 729 return ntpq_decodeaddrtype(&dum_store); 730 731 return -1; 732 } 733 734 735 736 /***************************************************************************** 737 * 738 * ntpq_get_assoc_clockvars 739 * 740 * With this function the clock variables of the specified association are 741 * requested from a NTP host. This makes only sense for associations with 742 * the type 'l' (Local Clock) and you should check this with 743 * ntpq_get_assoc_clocktype for each association, before you use this function 744 * on it. 745 * 746 **************************************************************************** 747 * Parameters: 748 * associd int requested associaton ID 749 * 750 * Returns: 751 * int 1 (one) if the clockvars have been read 752 * - OR - 753 * 0 (zero) if an error occured and the variable set 754 * could not be read 755 ****************************************************************************/ 756 int ntpq_get_assoc_clockvars( associd_t associd ) 757 { 758 if (NTP_CLOCKTYPE_LOCAL != ntpq_get_assoc_clocktype( 759 ntpq_get_assoc_number(associd))) 760 return 0; 761 clockvarlen = ntpq_read_assoc_clockvars( associd, clockvars, 762 sizeof(clockvars) ); 763 if ( clockvarlen <= 0 ) { 764 clockvar_assoc = 0; 765 return 0; 766 } else { 767 clockvar_assoc = associd; 768 return 1; 769 } 770 } 771 772 773