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