1 /* $NetBSD: libntpq.c,v 1.4 2016/01/08 21:35:40 christos Exp $ */ 2 3 /***************************************************************************** 4 * 5 * libntpq.c 6 * 7 * This is the wrapper library for ntpq, the NTP query utility. 8 * This library reuses the sourcecode from ntpq and exports a number 9 * of useful functions in a library that can be linked against applications 10 * that need to query the status of a running ntpd. The whole 11 * communcation is based on mode 6 packets. 12 * 13 ****************************************************************************/ 14 #define LIBNTPQ_C 15 #define NO_MAIN_ALLOWED 1 16 /* #define BUILD_AS_LIB Already provided by the Makefile */ 17 18 #include "ntpq.c" 19 #include "libntpq.h" 20 21 /* Function Prototypes */ 22 23 24 const char *Version = "libntpq 0.3beta"; 25 26 /* global variables used for holding snapshots of data */ 27 char peervars[NTPQ_BUFLEN]; 28 int peervarlen = 0; 29 associd_t peervar_assoc = 0; 30 char clockvars[NTPQ_BUFLEN]; 31 int clockvarlen = 0; 32 int clockvar_assoc = 0; 33 char sysvars[NTPQ_BUFLEN]; 34 int sysvarlen = 0; 35 char *ntpq_resultbuffer[NTPQ_BUFLEN]; 36 unsigned short ntpq_associations[MAXASSOC]; 37 struct ntpq_varlist ntpq_varlist[MAXLIST]; 38 39 /***************************************************************************** 40 * 41 * ntpq_stripquotes 42 * 43 * Parses a given character buffer srcbuf and removes all quoted 44 * characters. The resulting string is copied to the specified 45 * resultbuf character buffer. E.g. \" will be translated into " 46 * 47 **************************************************************************** 48 * Parameters: 49 * resultbuf char* The resulting string without quoted 50 * characters 51 * srcbuf char* The buffer holding the original string 52 * datalen int The number of bytes stored in srcbuf 53 * maxlen int Max. number of bytes for resultbuf 54 * 55 * Returns: 56 * int number of chars that have been copied to 57 * resultbuf 58 ****************************************************************************/ 59 60 int ntpq_stripquotes ( char *resultbuf, char *srcbuf, int datalen, int maxlen ) 61 { 62 char* tmpbuf = srcbuf; 63 64 while ( *tmpbuf != 0 ) 65 { 66 if ( *tmpbuf == '\"' ) 67 { 68 tmpbuf++; 69 continue; 70 } 71 72 if ( *tmpbuf == '\\' ) 73 { 74 tmpbuf++; 75 switch ( *tmpbuf ) 76 { 77 /* ignore if end of string */ 78 case 0: 79 continue; 80 /* skip and do not copy */ 81 case '\"': /* quotes */ 82 case 'n': /*newline*/ 83 case 'r': /*carriage return*/ 84 case 'g': /*bell*/ 85 case 't': /*tab*/ 86 tmpbuf++; 87 continue; 88 } 89 } 90 91 *resultbuf++ = *tmpbuf++; 92 93 } 94 95 *resultbuf = 0; 96 return strlen(resultbuf); 97 } 98 99 100 /***************************************************************************** 101 * 102 * ntpq_getvar 103 * 104 * This function parses a given buffer for a variable/value pair and 105 * copies the value of the requested variable into the specified 106 * varvalue buffer. 107 * 108 * It returns the number of bytes copied or zero for an empty result 109 * (=no matching variable found or empty value) 110 * 111 **************************************************************************** 112 * Parameters: 113 * resultbuf char* The resulting string without quoted 114 * characters 115 * datalen size_t The number of bytes stored in 116 * resultbuf 117 * varname char* Name of the required variable 118 * varvalue char* Where the value of the variable should 119 * be stored 120 * maxlen size_t Max. number of bytes for varvalue 121 * 122 * Returns: 123 * size_t number of chars that have been copied to 124 * varvalue 125 ****************************************************************************/ 126 127 size_t 128 ntpq_getvar( 129 const char * resultbuf, 130 size_t datalen, 131 const char * varname, 132 char * varvalue, 133 size_t maxlen) 134 { 135 char * name; 136 char * value; 137 size_t idatalen; 138 139 value = NULL; 140 idatalen = (int)datalen; 141 142 while (nextvar(&idatalen, &resultbuf, &name, &value)) { 143 if (strcmp(varname, name) == 0) { 144 ntpq_stripquotes(varvalue, value, strlen(value), maxlen); 145 146 return strlen(varvalue); 147 } 148 } 149 150 return 0; 151 } 152 153 154 /***************************************************************************** 155 * 156 * ntpq_queryhost 157 * 158 * Sends a mode 6 query packet to the current open host (see 159 * ntpq_openhost) and stores the requested variable set in the specified 160 * character buffer. 161 * It returns the number of bytes read or zero for an empty result 162 * (=no answer or empty value) 163 * 164 **************************************************************************** 165 * Parameters: 166 * VARSET u_short Which variable set should be 167 * read (PEERVARS or CLOCKVARS) 168 * association int The association ID that should be read 169 * 0 represents the ntpd instance itself 170 * resultbuf char* The resulting string without quoted 171 * characters 172 * maxlen int Max. number of bytes for varvalue 173 * 174 * Returns: 175 * int number of bytes that have been copied to 176 * resultbuf 177 * - OR - 178 * 0 (zero) if no reply has been received or 179 * another failure occured 180 ****************************************************************************/ 181 182 int ntpq_queryhost(unsigned short VARSET, unsigned short association, char *resultbuf, int maxlen) 183 { 184 const char *datap; 185 int res; 186 size_t dsize; 187 u_short rstatus; 188 189 if ( numhosts > 0 ) 190 res = doquery(VARSET,association,0,0, (char *)0, &rstatus, &dsize, &datap); 191 else 192 return 0; 193 194 if ( ( res != 0) || ( dsize == 0 ) ) /* no data */ 195 return 0; 196 197 if ( dsize > maxlen) 198 dsize = maxlen; 199 200 201 /* fill result resultbuf */ 202 memcpy(resultbuf, datap, dsize); 203 204 return dsize; 205 } 206 207 208 209 /***************************************************************************** 210 * 211 * ntpq_openhost 212 * 213 * Sets up a connection to the ntpd instance of a specified host. Note: 214 * There is no real "connection" established because NTP solely works 215 * based on UDP. 216 * 217 **************************************************************************** 218 * Parameters: 219 * hostname char* Hostname/IP of the host running ntpd 220 * fam int Address Family (AF_INET, AF_INET6, or 0) 221 * 222 * Returns: 223 * int 1 if the host connection could be set up, i.e. 224 * name resolution was succesful and/or IP address 225 * has been validated 226 * - OR - 227 * 0 (zero) if a failure occured 228 ****************************************************************************/ 229 230 int 231 ntpq_openhost( 232 char *hostname, 233 int fam 234 ) 235 { 236 if ( openhost(hostname, fam) ) 237 { 238 numhosts = 1; 239 } else { 240 numhosts = 0; 241 } 242 243 return numhosts; 244 245 } 246 247 248 /***************************************************************************** 249 * 250 * ntpq_closehost 251 * 252 * Cleans up a connection by closing the used socket. Should be called 253 * when no further queries are required for the currently used host. 254 * 255 **************************************************************************** 256 * Parameters: 257 * - none - 258 * 259 * Returns: 260 * int 0 (zero) if no host has been opened before 261 * - OR - 262 * the resultcode from the closesocket function call 263 ****************************************************************************/ 264 265 int ntpq_closehost(void) 266 { 267 if ( numhosts ) 268 return closesocket(sockfd); 269 270 return 0; 271 } 272 273 274 /***************************************************************************** 275 * 276 * ntpq_read_associations 277 * 278 * This function queries the ntp host for its associations and returns the 279 * number of associations found. 280 * 281 * It takes an u_short array as its first parameter, this array holds the 282 * IDs of the associations, 283 * the function will not write more entries than specified with the 284 * max_entries parameter. 285 * 286 * However, if more than max_entries associations were found, the return 287 * value of this function will reflect the real number, even if not all 288 * associations have been stored in the array. 289 * 290 **************************************************************************** 291 * Parameters: 292 * resultbuf u_short*Array that should hold the list of 293 * association IDs 294 * maxentries int maximum number of association IDs that can 295 * be stored in resultbuf 296 * 297 * Returns: 298 * int number of association IDs stored in resultbuf 299 * - OR - 300 * 0 (zero) if a failure occured or no association has 301 * been returned. 302 ****************************************************************************/ 303 304 int ntpq_read_associations ( u_short resultbuf[], int max_entries ) 305 { 306 int i = 0; 307 308 if (ntpq_dogetassoc()) { 309 310 if(numassoc < max_entries) 311 max_entries = numassoc; 312 313 for (i=0;i<max_entries;i++) 314 resultbuf[i] = assoc_cache[i].assid; 315 316 return numassoc; 317 } 318 319 return 0; 320 } 321 322 323 324 325 /***************************************************************************** 326 * 327 * ntpq_get_assocs 328 * 329 * This function reads the associations of a previously selected (with 330 * ntpq_openhost) NTP host into its own (global) array and returns the 331 * number of associations found. 332 * 333 * The obtained association IDs can be read by using the ntpq_get_assoc_id 334 * function. 335 * 336 **************************************************************************** 337 * Parameters: 338 * - none - 339 * 340 * Returns: 341 * int number of association IDs stored in resultbuf 342 * - OR - 343 * 0 (zero) if a failure occured or no association has 344 * been returned. 345 ****************************************************************************/ 346 347 int ntpq_get_assocs ( void ) 348 { 349 return ntpq_read_associations( ntpq_associations, MAXASSOC ); 350 } 351 352 353 /***************************************************************************** 354 * 355 * ntpq_get_assoc_number 356 * 357 * This function returns for a given Association ID the association number 358 * in the internal association array, which is filled by the ntpq_get_assocs 359 * function. 360 * 361 **************************************************************************** 362 * Parameters: 363 * associd int requested associaton ID 364 * 365 * Returns: 366 * int the number of the association array element that is 367 * representing the given association ID 368 * - OR - 369 * -1 if a failure occured or no matching association 370 * ID has been found 371 ****************************************************************************/ 372 373 int ntpq_get_assoc_number ( associd_t associd ) 374 { 375 int i; 376 377 for (i=0;i<numassoc;i++) { 378 if (assoc_cache[i].assid == associd) 379 return i; 380 } 381 382 return -1; 383 384 } 385 386 387 /***************************************************************************** 388 * 389 * ntpq_read_assoc_peervars 390 * 391 * This function reads the peervars variable-set of a specified association 392 * from a NTP host and writes it to the result buffer specified, honoring 393 * the maxsize limit. 394 * 395 * It returns the number of bytes written or 0 when the variable-set is 396 * empty or failed to read. 397 * 398 **************************************************************************** 399 * Parameters: 400 * associd int requested associaton ID 401 * resultbuf char* character buffer where the variable set 402 * should be stored 403 * maxsize int the maximum number of bytes that can be 404 * written to resultbuf 405 * 406 * Returns: 407 * int number of chars that have been copied to 408 * resultbuf 409 * - OR - 410 * 0 (zero) if an error occured 411 ****************************************************************************/ 412 413 int 414 ntpq_read_assoc_peervars( 415 associd_t associd, 416 char * resultbuf, 417 int maxsize 418 ) 419 { 420 const char * datap; 421 int res; 422 size_t dsize; 423 u_short rstatus; 424 425 res = doquery(CTL_OP_READVAR, associd, 0, 0, NULL, &rstatus, 426 &dsize, &datap); 427 if (res != 0) 428 return 0; 429 if (dsize <= 0) { 430 if (numhosts > 1) 431 fprintf(stderr, "server=%s ", currenthost); 432 fprintf(stderr, 433 "***No information returned for association %d\n", 434 associd); 435 436 return 0; 437 } 438 if (dsize > maxsize) 439 dsize = maxsize; 440 memcpy(resultbuf, datap, dsize); 441 442 return dsize; 443 } 444 445 446 447 448 /***************************************************************************** 449 * 450 * ntpq_read_sysvars 451 * 452 * This function reads the sysvars variable-set from a NTP host and writes it 453 * to the result buffer specified, honoring the maxsize limit. 454 * 455 * It returns the number of bytes written or 0 when the variable-set is empty 456 * or could not be read. 457 * 458 **************************************************************************** 459 * Parameters: 460 * resultbuf char* character buffer where the variable set 461 * should be stored 462 * maxsize int the maximum number of bytes that can be 463 * written to resultbuf 464 * 465 * Returns: 466 * int number of chars that have been copied to 467 * resultbuf 468 * - OR - 469 * 0 (zero) if an error occured 470 ****************************************************************************/ 471 size_t 472 ntpq_read_sysvars( 473 char * resultbuf, 474 size_t maxsize 475 ) 476 { 477 const char * datap; 478 int res; 479 size_t dsize; 480 u_short rstatus; 481 482 res = doquery(CTL_OP_READVAR, 0, 0, 0, NULL, &rstatus, 483 &dsize, &datap); 484 485 if (res != 0) 486 return 0; 487 488 if (dsize == 0) { 489 if (numhosts > 1) 490 fprintf(stderr, "server=%s ", currenthost); 491 fprintf(stderr, "***No sysvar information returned\n"); 492 493 return 0; 494 } else { 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 size_t 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