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