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 int 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 int 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 int 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 int i_dsize; 478 size_t dsize; 479 u_short rstatus; 480 481 res = doquery(CTL_OP_READVAR, 0, 0, 0, NULL, &rstatus, 482 &i_dsize, &datap); 483 484 if (res != 0) 485 return 0; 486 487 if (i_dsize == 0) { 488 if (numhosts > 1) 489 fprintf(stderr, "server=%s ", currenthost); 490 fprintf(stderr, "***No sysvar information returned\n"); 491 492 return 0; 493 } else { 494 dsize = max(0, i_dsize); 495 dsize = min(dsize, maxsize); 496 memcpy(resultbuf, datap, dsize); 497 } 498 499 return dsize; 500 } 501 502 503 /***************************************************************************** 504 * ntpq_get_assoc_allvars 505 * 506 * With this function all association variables for the specified association 507 * ID can be requested from a NTP host. They are stored internally and can be 508 * read by using the ntpq_get_peervar or ntpq_get_clockvar functions. 509 * 510 * Basically this is only a combination of the ntpq_get_assoc_peervars and 511 * ntpq_get_assoc_clockvars functions. 512 * 513 * It returns 1 if both variable-sets (peervars and clockvars) were 514 * received successfully. If one variable-set or both of them weren't 515 * received, 516 * 517 **************************************************************************** 518 * Parameters: 519 * associd int requested associaton ID 520 * 521 * Returns: 522 * int nonzero if at least one variable set could be read 523 * - OR - 524 * 0 (zero) if an error occured and both variable sets 525 * could not be read 526 ****************************************************************************/ 527 int ntpq_get_assoc_allvars( associd_t associd ) 528 { 529 return ntpq_get_assoc_peervars ( associd ) & 530 ntpq_get_assoc_clockvars( associd ); 531 } 532 533 534 535 536 /***************************************************************************** 537 * 538 * ntpq_get_sysvars 539 * 540 * The system variables of a NTP host can be requested by using this function 541 * and afterwards using ntpq_get_sysvar to read the single variable values. 542 * 543 **************************************************************************** 544 * Parameters: 545 * - none - 546 * 547 * Returns: 548 * int nonzero if the variable set could be read 549 * - OR - 550 * 0 (zero) if an error occured and the sysvars 551 * could not be read 552 ****************************************************************************/ 553 int 554 ntpq_get_sysvars(void) 555 { 556 sysvarlen = ntpq_read_sysvars(sysvars, sizeof(sysvars)); 557 if (sysvarlen <= 0) 558 return 0; 559 else 560 return 1; 561 } 562 563 564 /***************************************************************************** 565 * 566 * ntp_get_peervar 567 * 568 * This function uses the variable-set which was read by using 569 * ntp_get_peervars and searches for a variable specified with varname. If 570 * such a variable exists, it writes its value into 571 * varvalue (maxlen specifies the size of this target buffer). 572 * 573 **************************************************************************** 574 * Parameters: 575 * varname char* requested variable name 576 * varvalue char* the buffer where the value should go into 577 * maxlen int maximum number of bytes that can be copied to 578 * varvalue 579 * 580 * Returns: 581 * int number of bytes copied to varvalue 582 * - OR - 583 * 0 (zero) if an error occured or the variable could 584 * not be found 585 ****************************************************************************/ 586 int ntpq_get_peervar( const char *varname, char *varvalue, int maxlen) 587 { 588 return ( ntpq_getvar(peervars,peervarlen,varname,varvalue,maxlen) ); 589 } 590 591 592 593 /***************************************************************************** 594 * 595 * ntpq_get_assoc_peervars 596 * 597 * This function requests the peer variables of the specified association 598 * from a NTP host. In order to access the variable values, the function 599 * ntpq_get_peervar must be used. 600 * 601 **************************************************************************** 602 * Parameters: 603 * associd int requested associaton ID 604 * 605 * Returns: 606 * int 1 (one) if the peervars have been read 607 * - OR - 608 * 0 (zero) if an error occured and the variable set 609 * could not be read 610 ****************************************************************************/ 611 int 612 ntpq_get_assoc_peervars( 613 associd_t associd 614 ) 615 { 616 peervarlen = ntpq_read_assoc_peervars(associd, peervars, 617 sizeof(peervars)); 618 if (peervarlen <= 0) { 619 peervar_assoc = 0; 620 621 return 0; 622 } 623 peervar_assoc = associd; 624 625 return 1; 626 } 627 628 629 /***************************************************************************** 630 * 631 * ntp_read_assoc_clockvars 632 * 633 * This function reads the clockvars variable-set of a specified association 634 * from a NTP host and writes it to the result buffer specified, honoring 635 * the maxsize limit. 636 * 637 * It returns the number of bytes written or 0 when the variable-set is 638 * empty or failed to read. 639 * 640 **************************************************************************** 641 * Parameters: 642 * associd int requested associaton ID 643 * resultbuf char* character buffer where the variable set 644 * should be stored 645 * maxsize int the maximum number of bytes that can be 646 * written to resultbuf 647 * 648 * Returns: 649 * int number of chars that have been copied to 650 * resultbuf 651 * - OR - 652 * 0 (zero) if an error occured 653 ****************************************************************************/ 654 655 int 656 ntpq_read_assoc_clockvars( 657 associd_t associd, 658 char * resultbuf, 659 int maxsize 660 ) 661 { 662 const char *datap; 663 int res; 664 int dsize; 665 u_short rstatus; 666 667 res = ntpq_doquerylist(ntpq_varlist, CTL_OP_READCLOCK, associd, 668 0, &rstatus, &dsize, &datap); 669 if (res != 0) 670 return 0; 671 672 if (dsize == 0) { 673 if (numhosts > 1) /* no information returned from server */ 674 return 0; 675 } else { 676 if (dsize > maxsize) 677 dsize = maxsize; 678 memcpy(resultbuf, datap, dsize); 679 } 680 681 return dsize; 682 } 683 684 685 686 /***************************************************************************** 687 * 688 * ntpq_get_assoc_clocktype 689 * 690 * This function returns a clocktype value for a given association number 691 * (not ID!): 692 * 693 * NTP_CLOCKTYPE_UNKNOWN Unknown clock type 694 * NTP_CLOCKTYPE_BROADCAST Broadcast server 695 * NTP_CLOCKTYPE_LOCAL Local clock 696 * NTP_CLOCKTYPE_UNICAST Unicast server 697 * NTP_CLOCKTYPE_MULTICAST Multicast server 698 * 699 ****************************************************************************/ 700 int 701 ntpq_get_assoc_clocktype( 702 int assoc_index 703 ) 704 { 705 associd_t associd; 706 int i; 707 int rc; 708 sockaddr_u dum_store; 709 char dstadr[LENHOSTNAME]; 710 char resultbuf[NTPQ_BUFLEN]; 711 712 if (assoc_index < 0 || assoc_index >= numassoc) 713 return -1; 714 715 associd = assoc_cache[assoc_index].assid; 716 if (associd == peervar_assoc) { 717 rc = ntpq_get_peervar("dstadr", dstadr, sizeof(dstadr)); 718 } else { 719 i = ntpq_read_assoc_peervars(associd, resultbuf, 720 sizeof(resultbuf)); 721 if (i <= 0) 722 return -1; 723 rc = ntpq_getvar(resultbuf, i, "dstadr", dstadr, 724 sizeof(dstadr)); 725 } 726 727 if (0 != rc && decodenetnum(dstadr, &dum_store)) 728 return ntpq_decodeaddrtype(&dum_store); 729 730 return -1; 731 } 732 733 734 735 /***************************************************************************** 736 * 737 * ntpq_get_assoc_clockvars 738 * 739 * With this function the clock variables of the specified association are 740 * requested from a NTP host. This makes only sense for associations with 741 * the type 'l' (Local Clock) and you should check this with 742 * ntpq_get_assoc_clocktype for each association, before you use this function 743 * on it. 744 * 745 **************************************************************************** 746 * Parameters: 747 * associd int requested associaton ID 748 * 749 * Returns: 750 * int 1 (one) if the clockvars have been read 751 * - OR - 752 * 0 (zero) if an error occured and the variable set 753 * could not be read 754 ****************************************************************************/ 755 int ntpq_get_assoc_clockvars( associd_t associd ) 756 { 757 if (NTP_CLOCKTYPE_LOCAL != ntpq_get_assoc_clocktype( 758 ntpq_get_assoc_number(associd))) 759 return 0; 760 clockvarlen = ntpq_read_assoc_clockvars( associd, clockvars, 761 sizeof(clockvars) ); 762 if ( clockvarlen <= 0 ) { 763 clockvar_assoc = 0; 764 return 0; 765 } else { 766 clockvar_assoc = associd; 767 return 1; 768 } 769 } 770 771 772