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