1 /* $NetBSD: libntpq.c,v 1.1.1.2 2012/01/31 21:26:52 kardel 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 * 221 * Returns: 222 * int 1 if the host connection could be set up, i.e. 223 * name resolution was succesful and/or IP address 224 * has been validated 225 * - OR - 226 * 0 (zero) if a failure occured 227 ****************************************************************************/ 228 229 int ntpq_openhost(char *hostname) 230 { 231 if ( openhost(hostname) ) 232 { 233 numhosts = 1; 234 } else { 235 numhosts = 0; 236 } 237 238 return numhosts; 239 240 } 241 242 243 /***************************************************************************** 244 * 245 * ntpq_closehost 246 * 247 * Cleans up a connection by closing the used socket. Should be called 248 * when no further queries are required for the currently used host. 249 * 250 **************************************************************************** 251 * Parameters: 252 * - none - 253 * 254 * Returns: 255 * int 0 (zero) if no host has been opened before 256 * - OR - 257 * the resultcode from the closesocket function call 258 ****************************************************************************/ 259 260 int ntpq_closehost(void) 261 { 262 if ( numhosts ) 263 return closesocket(sockfd); 264 265 return 0; 266 } 267 268 269 /***************************************************************************** 270 * 271 * ntpq_read_associations 272 * 273 * This function queries the ntp host for its associations and returns the 274 * number of associations found. 275 * 276 * It takes an u_short array as its first parameter, this array holds the 277 * IDs of the associations, 278 * the function will not write more entries than specified with the 279 * max_entries parameter. 280 * 281 * However, if more than max_entries associations were found, the return 282 * value of this function will reflect the real number, even if not all 283 * associations have been stored in the array. 284 * 285 **************************************************************************** 286 * Parameters: 287 * resultbuf u_short*Array that should hold the list of 288 * association IDs 289 * maxentries int maximum number of association IDs that can 290 * be stored in resultbuf 291 * 292 * Returns: 293 * int number of association IDs stored in resultbuf 294 * - OR - 295 * 0 (zero) if a failure occured or no association has 296 * been returned. 297 ****************************************************************************/ 298 299 int ntpq_read_associations ( u_short resultbuf[], int max_entries ) 300 { 301 int i = 0; 302 303 if (ntpq_dogetassoc()) { 304 305 if(numassoc < max_entries) 306 max_entries = numassoc; 307 308 for (i=0;i<max_entries;i++) 309 resultbuf[i] = assoc_cache[i].assid; 310 311 return numassoc; 312 } 313 314 return 0; 315 } 316 317 318 319 320 /***************************************************************************** 321 * 322 * ntpq_get_assocs 323 * 324 * This function reads the associations of a previously selected (with 325 * ntpq_openhost) NTP host into its own (global) array and returns the 326 * number of associations found. 327 * 328 * The obtained association IDs can be read by using the ntpq_get_assoc_id 329 * function. 330 * 331 **************************************************************************** 332 * Parameters: 333 * - none - 334 * 335 * Returns: 336 * int number of association IDs stored in resultbuf 337 * - OR - 338 * 0 (zero) if a failure occured or no association has 339 * been returned. 340 ****************************************************************************/ 341 342 int ntpq_get_assocs ( void ) 343 { 344 return ntpq_read_associations( ntpq_associations, MAXASSOC ); 345 } 346 347 348 /***************************************************************************** 349 * 350 * ntpq_get_assoc_number 351 * 352 * This function returns for a given Association ID the association number 353 * in the internal association array, which is filled by the ntpq_get_assocs 354 * function. 355 * 356 **************************************************************************** 357 * Parameters: 358 * associd int requested associaton ID 359 * 360 * Returns: 361 * int the number of the association array element that is 362 * representing the given association ID 363 * - OR - 364 * -1 if a failure occured or no matching association 365 * ID has been found 366 ****************************************************************************/ 367 368 int ntpq_get_assoc_number ( associd_t associd ) 369 { 370 int i; 371 372 for (i=0;i<numassoc;i++) { 373 if (assoc_cache[i].assid == associd) 374 return i; 375 } 376 377 return -1; 378 379 } 380 381 382 /***************************************************************************** 383 * 384 * ntpq_read_assoc_peervars 385 * 386 * This function reads the peervars variable-set of a specified association 387 * from a NTP host and writes it to the result buffer specified, honoring 388 * the maxsize limit. 389 * 390 * It returns the number of bytes written or 0 when the variable-set is 391 * empty or failed to read. 392 * 393 **************************************************************************** 394 * Parameters: 395 * associd int requested associaton ID 396 * resultbuf char* character buffer where the variable set 397 * should be stored 398 * maxsize int the maximum number of bytes that can be 399 * written to resultbuf 400 * 401 * Returns: 402 * int number of chars that have been copied to 403 * resultbuf 404 * - OR - 405 * 0 (zero) if an error occured 406 ****************************************************************************/ 407 408 int 409 ntpq_read_assoc_peervars( 410 associd_t associd, 411 char * resultbuf, 412 int maxsize 413 ) 414 { 415 const char * datap; 416 int res; 417 int dsize; 418 u_short rstatus; 419 420 res = doquery(CTL_OP_READVAR, associd, 0, 0, NULL, &rstatus, 421 &dsize, &datap); 422 if (res != 0) 423 return 0; 424 if (dsize <= 0) { 425 if (numhosts > 1) 426 fprintf(stderr, "server=%s ", currenthost); 427 fprintf(stderr, 428 "***No information returned for association %d\n", 429 associd); 430 431 return 0; 432 } 433 if (dsize > maxsize) 434 dsize = maxsize; 435 memcpy(resultbuf, datap, dsize); 436 437 return dsize; 438 } 439 440 441 442 443 /***************************************************************************** 444 * 445 * ntpq_read_sysvars 446 * 447 * This function reads the sysvars variable-set from a NTP host and writes it 448 * to the result buffer specified, honoring the maxsize limit. 449 * 450 * It returns the number of bytes written or 0 when the variable-set is empty 451 * or could not be read. 452 * 453 **************************************************************************** 454 * Parameters: 455 * resultbuf char* character buffer where the variable set 456 * should be stored 457 * maxsize int the maximum number of bytes that can be 458 * written to resultbuf 459 * 460 * Returns: 461 * int number of chars that have been copied to 462 * resultbuf 463 * - OR - 464 * 0 (zero) if an error occured 465 ****************************************************************************/ 466 size_t 467 ntpq_read_sysvars( 468 char * resultbuf, 469 size_t maxsize 470 ) 471 { 472 const char * datap; 473 int res; 474 int i_dsize; 475 size_t dsize; 476 u_short rstatus; 477 478 res = doquery(CTL_OP_READVAR, 0, 0, 0, NULL, &rstatus, 479 &i_dsize, &datap); 480 481 if (res != 0) 482 return 0; 483 484 if (i_dsize == 0) { 485 if (numhosts > 1) 486 fprintf(stderr, "server=%s ", currenthost); 487 fprintf(stderr, "***No sysvar information returned\n"); 488 489 return 0; 490 } else { 491 dsize = max(0, i_dsize); 492 dsize = min(dsize, maxsize); 493 memcpy(resultbuf, datap, dsize); 494 } 495 496 return dsize; 497 } 498 499 500 /***************************************************************************** 501 * ntpq_get_assoc_allvars 502 * 503 * With this function all association variables for the specified association 504 * ID can be requested from a NTP host. They are stored internally and can be 505 * read by using the ntpq_get_peervar or ntpq_get_clockvar functions. 506 * 507 * Basically this is only a combination of the ntpq_get_assoc_peervars and 508 * ntpq_get_assoc_clockvars functions. 509 * 510 * It returns 1 if both variable-sets (peervars and clockvars) were 511 * received successfully. If one variable-set or both of them weren't 512 * received, 513 * 514 **************************************************************************** 515 * Parameters: 516 * associd int requested associaton ID 517 * 518 * Returns: 519 * int nonzero if at least one variable set could be read 520 * - OR - 521 * 0 (zero) if an error occured and both variable sets 522 * could not be read 523 ****************************************************************************/ 524 int ntpq_get_assoc_allvars( associd_t associd ) 525 { 526 return ntpq_get_assoc_peervars ( associd ) & 527 ntpq_get_assoc_clockvars( associd ); 528 } 529 530 531 532 533 /***************************************************************************** 534 * 535 * ntpq_get_sysvars 536 * 537 * The system variables of a NTP host can be requested by using this function 538 * and afterwards using ntpq_get_sysvar to read the single variable values. 539 * 540 **************************************************************************** 541 * Parameters: 542 * - none - 543 * 544 * Returns: 545 * int nonzero if the variable set could be read 546 * - OR - 547 * 0 (zero) if an error occured and the sysvars 548 * could not be read 549 ****************************************************************************/ 550 int 551 ntpq_get_sysvars(void) 552 { 553 sysvarlen = ntpq_read_sysvars(sysvars, sizeof(sysvars)); 554 if (sysvarlen <= 0) 555 return 0; 556 else 557 return 1; 558 } 559 560 561 /***************************************************************************** 562 * 563 * ntp_get_peervar 564 * 565 * This function uses the variable-set which was read by using 566 * ntp_get_peervars and searches for a variable specified with varname. If 567 * such a variable exists, it writes its value into 568 * varvalue (maxlen specifies the size of this target buffer). 569 * 570 **************************************************************************** 571 * Parameters: 572 * varname char* requested variable name 573 * varvalue char* the buffer where the value should go into 574 * maxlen int maximum number of bytes that can be copied to 575 * varvalue 576 * 577 * Returns: 578 * int number of bytes copied to varvalue 579 * - OR - 580 * 0 (zero) if an error occured or the variable could 581 * not be found 582 ****************************************************************************/ 583 int ntpq_get_peervar( const char *varname, char *varvalue, int maxlen) 584 { 585 return ( ntpq_getvar(peervars,peervarlen,varname,varvalue,maxlen) ); 586 } 587 588 589 590 /***************************************************************************** 591 * 592 * ntpq_get_assoc_peervars 593 * 594 * This function requests the peer variables of the specified association 595 * from a NTP host. In order to access the variable values, the function 596 * ntpq_get_peervar must be used. 597 * 598 **************************************************************************** 599 * Parameters: 600 * associd int requested associaton ID 601 * 602 * Returns: 603 * int 1 (one) if the peervars have been read 604 * - OR - 605 * 0 (zero) if an error occured and the variable set 606 * could not be read 607 ****************************************************************************/ 608 int 609 ntpq_get_assoc_peervars( 610 associd_t associd 611 ) 612 { 613 peervarlen = ntpq_read_assoc_peervars(associd, peervars, 614 sizeof(peervars)); 615 if (peervarlen <= 0) { 616 peervar_assoc = 0; 617 618 return 0; 619 } 620 peervar_assoc = associd; 621 622 return 1; 623 } 624 625 626 /***************************************************************************** 627 * 628 * ntp_read_assoc_clockvars 629 * 630 * This function reads the clockvars variable-set of a specified association 631 * from a NTP host and writes it to the result buffer specified, honoring 632 * the maxsize limit. 633 * 634 * It returns the number of bytes written or 0 when the variable-set is 635 * empty or failed to read. 636 * 637 **************************************************************************** 638 * Parameters: 639 * associd int requested associaton ID 640 * resultbuf char* character buffer where the variable set 641 * should be stored 642 * maxsize int the maximum number of bytes that can be 643 * written to resultbuf 644 * 645 * Returns: 646 * int number of chars that have been copied to 647 * resultbuf 648 * - OR - 649 * 0 (zero) if an error occured 650 ****************************************************************************/ 651 652 int 653 ntpq_read_assoc_clockvars( 654 associd_t associd, 655 char * resultbuf, 656 int maxsize 657 ) 658 { 659 const char *datap; 660 int res; 661 int dsize; 662 u_short rstatus; 663 664 res = ntpq_doquerylist(ntpq_varlist, CTL_OP_READCLOCK, associd, 665 0, &rstatus, &dsize, &datap); 666 if (res != 0) 667 return 0; 668 669 if (dsize == 0) { 670 if (numhosts > 1) /* no information returned from server */ 671 return 0; 672 } else { 673 if (dsize > maxsize) 674 dsize = maxsize; 675 memcpy(resultbuf, datap, dsize); 676 } 677 678 return dsize; 679 } 680 681 682 683 /***************************************************************************** 684 * 685 * ntpq_get_assoc_clocktype 686 * 687 * This function returns a clocktype value for a given association number 688 * (not ID!): 689 * 690 * NTP_CLOCKTYPE_UNKNOWN Unknown clock type 691 * NTP_CLOCKTYPE_BROADCAST Broadcast server 692 * NTP_CLOCKTYPE_LOCAL Local clock 693 * NTP_CLOCKTYPE_UNICAST Unicast server 694 * NTP_CLOCKTYPE_MULTICAST Multicast server 695 * 696 ****************************************************************************/ 697 int 698 ntpq_get_assoc_clocktype( 699 int assoc_index 700 ) 701 { 702 associd_t associd; 703 int i; 704 int rc; 705 sockaddr_u dum_store; 706 char dstadr[LENHOSTNAME]; 707 char resultbuf[NTPQ_BUFLEN]; 708 709 if (assoc_index < 0 || assoc_index >= numassoc) 710 return -1; 711 712 associd = assoc_cache[assoc_index].assid; 713 if (associd == peervar_assoc) { 714 rc = ntpq_get_peervar("dstadr", dstadr, sizeof(dstadr)); 715 } else { 716 i = ntpq_read_assoc_peervars(associd, resultbuf, 717 sizeof(resultbuf)); 718 if (i <= 0) 719 return -1; 720 rc = ntpq_getvar(resultbuf, i, "dstadr", dstadr, 721 sizeof(dstadr)); 722 } 723 724 if (0 != rc && decodenetnum(dstadr, &dum_store)) 725 return ntpq_decodeaddrtype(&dum_store); 726 727 return -1; 728 } 729 730 731 732 /***************************************************************************** 733 * 734 * ntpq_get_assoc_clockvars 735 * 736 * With this function the clock variables of the specified association are 737 * requested from a NTP host. This makes only sense for associations with 738 * the type 'l' (Local Clock) and you should check this with 739 * ntpq_get_assoc_clocktype for each association, before you use this function 740 * on it. 741 * 742 **************************************************************************** 743 * Parameters: 744 * associd int requested associaton ID 745 * 746 * Returns: 747 * int 1 (one) if the clockvars have been read 748 * - OR - 749 * 0 (zero) if an error occured and the variable set 750 * could not be read 751 ****************************************************************************/ 752 int ntpq_get_assoc_clockvars( associd_t associd ) 753 { 754 if (NTP_CLOCKTYPE_LOCAL != ntpq_get_assoc_clocktype( 755 ntpq_get_assoc_number(associd))) 756 return 0; 757 clockvarlen = ntpq_read_assoc_clockvars( associd, clockvars, 758 sizeof(clockvars) ); 759 if ( clockvarlen <= 0 ) { 760 clockvar_assoc = 0; 761 return 0; 762 } else { 763 clockvar_assoc = associd; 764 return 1; 765 } 766 } 767 768 769