ntpq.c revision 294904
1178354Ssam/* 2178354Ssam * ntpq - query an NTP server using mode 6 commands 3178354Ssam */ 4178354Ssam#include <config.h> 5178354Ssam#include <stdio.h> 6178354Ssam#include <ctype.h> 7178354Ssam#include <signal.h> 8178354Ssam#include <setjmp.h> 9178354Ssam#include <sys/types.h> 10178354Ssam#include <sys/time.h> 11178354Ssam#ifdef HAVE_UNISTD_H 12178354Ssam# include <unistd.h> 13178354Ssam#endif 14178354Ssam#ifdef HAVE_FCNTL_H 15178354Ssam# include <fcntl.h> 16178354Ssam#endif 17178354Ssam#ifdef SYS_WINNT 18178354Ssam# include <mswsock.h> 19178354Ssam#endif 20178354Ssam#include <isc/net.h> 21178354Ssam#include <isc/result.h> 22178354Ssam 23178354Ssam#include "ntpq.h" 24178354Ssam#include "ntp_assert.h" 25178354Ssam#include "ntp_stdlib.h" 26178354Ssam#include "ntp_unixtime.h" 27178354Ssam#include "ntp_calendar.h" 28178354Ssam#include "ntp_select.h" 29178354Ssam#include "ntp_assert.h" 30178354Ssam#include "lib_strbuf.h" 31178354Ssam#include "ntp_lineedit.h" 32178354Ssam#include "ntp_debug.h" 33178354Ssam#ifdef OPENSSL 34178354Ssam#include "openssl/evp.h" 35178354Ssam#include "openssl/objects.h" 36178354Ssam#include "openssl/err.h" 37178354Ssam#endif 38178354Ssam#include <ssl_applink.c> 39178354Ssam 40178354Ssam#include "ntp_libopts.h" 41178354Ssam#include "ntpq-opts.h" 42178354Ssam#include "safecast.h" 43178354Ssam 44178354Ssam#ifdef SYS_VXWORKS /* vxWorks needs mode flag -casey*/ 45178354Ssam# define open(name, flags) open(name, flags, 0777) 46178354Ssam# define SERVER_PORT_NUM 123 47178354Ssam#endif 48178354Ssam 49178354Ssam/* we use COMMAND as an autogen keyword */ 50178354Ssam#ifdef COMMAND 51178354Ssam# undef COMMAND 52178354Ssam#endif 53178354Ssam 54178354Ssam/* 55178354Ssam * Because we potentially understand a lot of commands we will run 56178354Ssam * interactive if connected to a terminal. 57178354Ssam */ 58178354Ssamint interactive = 0; /* set to 1 when we should prompt */ 59178354Ssamconst char *prompt = "ntpq> "; /* prompt to ask him about */ 60190391Ssam 61190391Ssam/* 62190391Ssam * use old readvars behavior? --old-rv processing in ntpq resets 63178354Ssam * this value based on the presence or absence of --old-rv. It is 64178354Ssam * initialized to 1 here to maintain backward compatibility with 65178354Ssam * libntpq clients such as ntpsnmpd, which are free to reset it as 66178354Ssam * desired. 67178354Ssam */ 68178354Ssamint old_rv = 1; 69178354Ssam 70192468Ssam 71178354Ssam/* 72178354Ssam * for get_systime() 73178354Ssam */ 74192468Ssams_char sys_precision; /* local clock precision (log2 s) */ 75191546Ssam 76178354Ssam/* 77178354Ssam * Keyid used for authenticated requests. Obtained on the fly. 78178354Ssam */ 79178354Ssamu_long info_auth_keyid = 0; 80178354Ssam 81178354Ssamstatic int info_auth_keytype = NID_md5; /* MD5 */ 82178354Ssamstatic size_t info_auth_hashlen = 16; /* MD5 */ 83178354Ssamu_long current_time; /* needed by authkeys; not used */ 84178354Ssam 85178354Ssam/* 86178354Ssam * Flag which indicates we should always send authenticated requests 87178354Ssam */ 88178354Ssamint always_auth = 0; 89178354Ssam 90178354Ssam/* 91178354Ssam * Flag which indicates raw mode output. 92178354Ssam */ 93178354Ssamint rawmode = 0; 94178354Ssam 95178354Ssam/* 96178354Ssam * Packet version number we use 97178354Ssam */ 98178354Ssamu_char pktversion = NTP_OLDVERSION + 1; 99191546Ssam 100178354Ssam/* 101178354Ssam * Don't jump if no set jmp. 102241138Sadrian */ 103178354Ssamvolatile int jump = 0; 104178354Ssam 105178354Ssam/* 106178354Ssam * Format values 107178354Ssam */ 108178354Ssam#define PADDING 0 109178354Ssam#define HA 1 /* host address */ 110178354Ssam#define NA 2 /* network address */ 111178354Ssam#define LP 3 /* leap (print in binary) */ 112178354Ssam#define RF 4 /* refid (sometimes string, sometimes not) */ 113178354Ssam#define AR 5 /* array of times */ 114178354Ssam#define FX 6 /* test flags */ 115178354Ssam#define TS 7 /* l_fp timestamp in hex */ 116178354Ssam#define OC 8 /* integer, print in octal */ 117193413Ssam#define EOV 255 /* end of table */ 118193413Ssam 119193413Ssam/* 120193413Ssam * For the most part ntpq simply displays what ntpd provides in the 121193413Ssam * mostly plain-text mode 6 responses. A few variable names are by 122193413Ssam * default "cooked" to provide more human-friendly output. 123193413Ssam */ 124193413Ssamconst var_format cookedvars[] = { 125193413Ssam { "leap", LP }, 126193413Ssam { "reach", OC }, 127193413Ssam { "refid", RF }, 128193413Ssam { "reftime", TS }, 129193413Ssam { "clock", TS }, 130193414Ssam { "org", TS }, 131193414Ssam { "rec", TS }, 132193414Ssam { "xmt", TS }, 133193414Ssam { "flash", FX }, 134193414Ssam { "srcadr", HA }, 135193414Ssam { "peeradr", HA }, /* compat with others */ 136193414Ssam { "dstadr", NA }, 137193414Ssam { "filtdelay", AR }, 138193414Ssam { "filtoffset", AR }, 139178354Ssam { "filtdisp", AR }, 140193414Ssam { "filterror", AR }, /* compat with others */ 141193414Ssam}; 142193414Ssam 143193414Ssam 144193414Ssam 145193414Ssam/* 146193414Ssam * flasher bits 147193414Ssam */ 148193414Ssamstatic const char *tstflagnames[] = { 149193414Ssam "pkt_dup", /* TEST1 */ 150193414Ssam "pkt_bogus", /* TEST2 */ 151193414Ssam "pkt_unsync", /* TEST3 */ 152193414Ssam "pkt_denied", /* TEST4 */ 153178354Ssam "pkt_auth", /* TEST5 */ 154178354Ssam "pkt_stratum", /* TEST6 */ 155178354Ssam "pkt_header", /* TEST7 */ 156178354Ssam "pkt_autokey", /* TEST8 */ 157178354Ssam "pkt_crypto", /* TEST9 */ 158178354Ssam "peer_stratum", /* TEST10 */ 159178354Ssam "peer_dist", /* TEST11 */ 160178354Ssam "peer_loop", /* TEST12 */ 161178354Ssam "peer_unreach" /* TEST13 */ 162178354Ssam}; 163178354Ssam 164178354Ssam 165178354Ssamint ntpqmain (int, char **); 166178354Ssam/* 167178354Ssam * Built in command handler declarations 168178354Ssam */ 169178354Ssamstatic int openhost (const char *, int); 170178354Ssamstatic void dump_hex_printable(const void *, size_t); 171178354Ssamstatic int sendpkt (void *, size_t); 172178354Ssamstatic int getresponse (int, int, u_short *, size_t *, const char **, int); 173178354Ssamstatic int sendrequest (int, associd_t, int, size_t, const char *); 174178354Ssamstatic char * tstflags (u_long); 175178354Ssam#ifndef BUILD_AS_LIB 176178354Ssamstatic void getcmds (void); 177178354Ssam#ifndef SYS_WINNT 178178354Ssamstatic int abortcmd (void); 179178354Ssam#endif /* SYS_WINNT */ 180178354Ssamstatic void docmd (const char *); 181178354Ssamstatic void tokenize (const char *, char **, int *); 182178354Ssamstatic int getarg (const char *, int, arg_v *); 183178354Ssam#endif /* BUILD_AS_LIB */ 184178354Ssamstatic int findcmd (const char *, struct xcmd *, 185178354Ssam struct xcmd *, struct xcmd **); 186178354Ssamstatic int rtdatetolfp (char *, l_fp *); 187178354Ssamstatic int decodearr (char *, int *, l_fp *); 188178354Ssamstatic void help (struct parse *, FILE *); 189178354Ssamstatic int helpsort (const void *, const void *); 190178354Ssamstatic void printusage (struct xcmd *, FILE *); 191178354Ssamstatic void timeout (struct parse *, FILE *); 192178354Ssamstatic void auth_delay (struct parse *, FILE *); 193178354Ssamstatic void host (struct parse *, FILE *); 194178354Ssamstatic void ntp_poll (struct parse *, FILE *); 195178354Ssamstatic void keyid (struct parse *, FILE *); 196178354Ssamstatic void keytype (struct parse *, FILE *); 197178354Ssamstatic void passwd (struct parse *, FILE *); 198178354Ssamstatic void hostnames (struct parse *, FILE *); 199178354Ssamstatic void setdebug (struct parse *, FILE *); 200178354Ssamstatic void quit (struct parse *, FILE *); 201178354Ssamstatic void version (struct parse *, FILE *); 202178354Ssamstatic void raw (struct parse *, FILE *); 203178354Ssamstatic void cooked (struct parse *, FILE *); 204193655Ssamstatic void authenticate (struct parse *, FILE *); 205178354Ssamstatic void ntpversion (struct parse *, FILE *); 206178354Ssamstatic void warning (const char *, ...) 207178354Ssam __attribute__((__format__(__printf__, 1, 2))); 208178354Ssamstatic void error (const char *, ...) 209178354Ssam __attribute__((__format__(__printf__, 1, 2))); 210178354Ssamstatic u_long getkeyid (const char *); 211178354Ssamstatic void atoascii (const char *, size_t, char *, size_t); 212178354Ssamstatic void cookedprint (int, size_t, const char *, int, int, FILE *); 213178354Ssamstatic void rawprint (int, size_t, const char *, int, int, FILE *); 214178354Ssamstatic void startoutput (void); 215178354Ssamstatic void output (FILE *, const char *, const char *); 216178354Ssamstatic void endoutput (FILE *); 217178354Ssamstatic void outputarr (FILE *, char *, int, l_fp *); 218178354Ssamstatic int assoccmp (const void *, const void *); 219178354Ssamstatic void on_ctrlc (void); 220178354Ssam u_short varfmt (const char *); 221178354Ssamstatic int my_easprintf (char**, const char *, ...) NTP_PRINTF(2, 3); 222178354Ssamvoid ntpq_custom_opt_handler (tOptions *, tOptDesc *); 223178354Ssam 224178354Ssam#ifdef OPENSSL 225178354Ssam# ifdef HAVE_EVP_MD_DO_ALL_SORTED 226178354Ssamstatic void list_md_fn(const EVP_MD *m, const char *from, 227178354Ssam const char *to, void *arg ); 228178354Ssam# endif 229178354Ssam#endif 230178354Ssamstatic char *list_digest_names(void); 231178354Ssam 232178354Ssam/* 233178354Ssam * Built-in commands we understand 234178354Ssam */ 235178354Ssamstruct xcmd builtins[] = { 236178354Ssam { "?", help, { OPT|NTP_STR, NO, NO, NO }, 237178354Ssam { "command", "", "", "" }, 238178354Ssam "tell the use and syntax of commands" }, 239178354Ssam { "help", help, { OPT|NTP_STR, NO, NO, NO }, 240178354Ssam { "command", "", "", "" }, 241178354Ssam "tell the use and syntax of commands" }, 242178354Ssam { "timeout", timeout, { OPT|NTP_UINT, NO, NO, NO }, 243178354Ssam { "msec", "", "", "" }, 244178354Ssam "set the primary receive time out" }, 245178354Ssam { "delay", auth_delay, { OPT|NTP_INT, NO, NO, NO }, 246178354Ssam { "msec", "", "", "" }, 247178354Ssam "set the delay added to encryption time stamps" }, 248178354Ssam { "host", host, { OPT|NTP_STR, OPT|NTP_STR, NO, NO }, 249178354Ssam { "-4|-6", "hostname", "", "" }, 250178354Ssam "specify the host whose NTP server we talk to" }, 251178354Ssam { "poll", ntp_poll, { OPT|NTP_UINT, OPT|NTP_STR, NO, NO }, 252178354Ssam { "n", "verbose", "", "" }, 253178354Ssam "poll an NTP server in client mode `n' times" }, 254178354Ssam { "passwd", passwd, { OPT|NTP_STR, NO, NO, NO }, 255178354Ssam { "", "", "", "" }, 256178354Ssam "specify a password to use for authenticated requests"}, 257178354Ssam { "hostnames", hostnames, { OPT|NTP_STR, NO, NO, NO }, 258178354Ssam { "yes|no", "", "", "" }, 259178354Ssam "specify whether hostnames or net numbers are printed"}, 260178354Ssam { "debug", setdebug, { OPT|NTP_STR, NO, NO, NO }, 261178354Ssam { "no|more|less", "", "", "" }, 262178354Ssam "set/change debugging level" }, 263178354Ssam { "quit", quit, { NO, NO, NO, NO }, 264178354Ssam { "", "", "", "" }, 265178354Ssam "exit ntpq" }, 266178354Ssam { "exit", quit, { NO, NO, NO, NO }, 267178354Ssam { "", "", "", "" }, 268178354Ssam "exit ntpq" }, 269178354Ssam { "keyid", keyid, { OPT|NTP_UINT, NO, NO, NO }, 270178354Ssam { "key#", "", "", "" }, 271178354Ssam "set keyid to use for authenticated requests" }, 272178354Ssam { "version", version, { NO, NO, NO, NO }, 273178354Ssam { "", "", "", "" }, 274178354Ssam "print version number" }, 275193655Ssam { "raw", raw, { NO, NO, NO, NO }, 276178354Ssam { "", "", "", "" }, 277178354Ssam "do raw mode variable output" }, 278178354Ssam { "cooked", cooked, { NO, NO, NO, NO }, 279178354Ssam { "", "", "", "" }, 280178354Ssam "do cooked mode variable output" }, 281178354Ssam { "authenticate", authenticate, { OPT|NTP_STR, NO, NO, NO }, 282178354Ssam { "yes|no", "", "", "" }, 283178354Ssam "always authenticate requests to this server" }, 284178354Ssam { "ntpversion", ntpversion, { OPT|NTP_UINT, NO, NO, NO }, 285178354Ssam { "version number", "", "", "" }, 286178354Ssam "set the NTP version number to use for requests" }, 287193413Ssam { "keytype", keytype, { OPT|NTP_STR, NO, NO, NO }, 288193413Ssam { "key type %s", "", "", "" }, 289193413Ssam NULL }, 290193413Ssam { 0, 0, { NO, NO, NO, NO }, 291193413Ssam { "", "", "", "" }, "" } 292178354Ssam}; 293178354Ssam 294178354Ssam 295178354Ssam/* 296178354Ssam * Default values we use. 297178354Ssam */ 298178354Ssam#define DEFHOST "localhost" /* default host name */ 299178354Ssam#define DEFTIMEOUT 5 /* wait 5 seconds for 1st pkt */ 300178354Ssam#define DEFSTIMEOUT 3 /* and 3 more for each additional */ 301178354Ssam/* 302178354Ssam * Requests are automatically retried once, so total timeout with no 303178354Ssam * response is a bit over 2 * DEFTIMEOUT, or 10 seconds. At the other 304178354Ssam * extreme, a request eliciting 32 packets of responses each for some 305178354Ssam * reason nearly DEFSTIMEOUT seconds after the prior in that series, 306178354Ssam * with a single packet dropped, would take around 32 * DEFSTIMEOUT, or 307178354Ssam * 93 seconds to fail each of two times, or 186 seconds. 308178354Ssam * Some commands involve a series of requests, such as "peers" and 309178354Ssam * "mrulist", so the cumulative timeouts are even longer for those. 310178354Ssam */ 311178354Ssam#define DEFDELAY 0x51EB852 /* 20 milliseconds, l_fp fraction */ 312178354Ssam#define LENHOSTNAME 256 /* host name is 256 characters long */ 313178354Ssam#define MAXCMDS 100 /* maximum commands on cmd line */ 314178354Ssam#define MAXHOSTS 200 /* maximum hosts on cmd line */ 315178354Ssam#define MAXLINE 512 /* maximum line length */ 316178354Ssam#define MAXTOKENS (1+MAXARGS+2) /* maximum number of usable tokens */ 317178354Ssam#define MAXVARLEN 256 /* maximum length of a variable name */ 318178354Ssam#define MAXVALLEN 2048 /* maximum length of a variable value */ 319178354Ssam#define MAXOUTLINE 72 /* maximum length of an output line */ 320178354Ssam#define SCREENWIDTH 76 /* nominal screen width in columns */ 321178354Ssam 322178354Ssam/* 323178354Ssam * Some variables used and manipulated locally 324178354Ssam */ 325178354Ssamstruct sock_timeval tvout = { DEFTIMEOUT, 0 }; /* time out for reads */ 326178354Ssamstruct sock_timeval tvsout = { DEFSTIMEOUT, 0 };/* secondary time out */ 327178354Ssaml_fp delay_time; /* delay time */ 328178354Ssamchar currenthost[LENHOSTNAME]; /* current host name */ 329178354Ssamint currenthostisnum; /* is prior text from IP? */ 330178354Ssamstruct sockaddr_in hostaddr; /* host address */ 331178354Ssamint showhostnames = 1; /* show host names by default */ 332193414Ssamint wideremote = 0; /* show wide remote names? */ 333193414Ssam 334193414Ssamint ai_fam_templ; /* address family */ 335193414Ssamint ai_fam_default; /* default address family */ 336193414SsamSOCKET sockfd; /* fd socket is opened on */ 337193414Ssamint havehost = 0; /* set to 1 when host open */ 338193414Ssamint s_port = 0; 339193414Ssamstruct servent *server_entry = NULL; /* server entry for ntp */ 340193414Ssam 341193414Ssam 342193414Ssam/* 343193414Ssam * Sequence number used for requests. It is incremented before 344178354Ssam * it is used. 345178354Ssam */ 346178354Ssamu_short sequence; 347178354Ssam 348178354Ssam/* 349178354Ssam * Holds data returned from queries. Declare buffer long to be sure of 350178354Ssam * alignment. 351178354Ssam */ 352178354Ssam#define DATASIZE (MAXFRAGS*480) /* maximum amount of data */ 353178354Ssamlong pktdata[DATASIZE/sizeof(long)]; 354178354Ssam 355178354Ssam/* 356178354Ssam * assoc_cache[] is a dynamic array which allows references to 357193292Ssam * associations using &1 ... &N for n associations, avoiding manual 358254640Sadrian * lookup of the current association IDs for a given ntpd. It also 359254523Sandre * caches the status word for each association, retrieved incidentally. 360254523Sandre */ 361254640Sadrianstruct association * assoc_cache; 362254640Sadrianu_int assoc_cache_slots;/* count of allocated array entries */ 363254640Sadrianu_int numassoc; /* number of cached associations */ 364193292Ssam 365178354Ssam/* 366178354Ssam * For commands typed on the command line (with the -c option) 367178354Ssam */ 368178354Ssamint numcmds = 0; 369178354Ssamconst char *ccmds[MAXCMDS]; 370178354Ssam#define ADDCMD(cp) if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp) 371178354Ssam 372178354Ssam/* 373178354Ssam * When multiple hosts are specified. 374178354Ssam */ 375178354Ssam 376178354Ssamu_int numhosts; 377178354Ssam 378178354Ssamchost chosts[MAXHOSTS]; 379178354Ssam#define ADDHOST(cp) \ 380178354Ssam do { \ 381178354Ssam if (numhosts < MAXHOSTS) { \ 382178354Ssam chosts[numhosts].name = (cp); \ 383178354Ssam chosts[numhosts].fam = ai_fam_templ; \ 384243882Sglebius numhosts++; \ 385178354Ssam } \ 386178354Ssam } while (0) 387178354Ssam 388178354Ssam/* 389178354Ssam * Macro definitions we use 390178354Ssam */ 391178354Ssam#define ISSPACE(c) ((c) == ' ' || (c) == '\t') 392178354Ssam#define ISEOL(c) ((c) == '\n' || (c) == '\r' || (c) == '\0') 393178354Ssam#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0) 394178354Ssam 395178354Ssam/* 396178354Ssam * Jump buffer for longjumping back to the command level 397178354Ssam */ 398178354Ssamjmp_buf interrupt_buf; 399178354Ssam 400178354Ssam/* 401178354Ssam * Points at file being currently printed into 402178354Ssam */ 403178354SsamFILE *current_output; 404178354Ssam 405178354Ssam/* 406178354Ssam * Command table imported from ntpdc_ops.c 407178354Ssam */ 408178354Ssamextern struct xcmd opcmds[]; 409178354Ssam 410178354Ssamchar const *progname; 411178354Ssam 412178354Ssam#ifdef NO_MAIN_ALLOWED 413178354Ssam#ifndef BUILD_AS_LIB 414178354SsamCALL(ntpq,"ntpq",ntpqmain); 415178354Ssam 416178354Ssamvoid clear_globals(void) 417178354Ssam{ 418178354Ssam extern int ntp_optind; 419178354Ssam showhostnames = 0; /* don'tshow host names by default */ 420254082Sadrian ntp_optind = 0; 421178354Ssam server_entry = NULL; /* server entry for ntp */ 422178354Ssam havehost = 0; /* set to 1 when host open */ 423178354Ssam numassoc = 0; /* number of cached associations */ 424178354Ssam numcmds = 0; 425178354Ssam numhosts = 0; 426178354Ssam} 427178354Ssam#endif /* !BUILD_AS_LIB */ 428178354Ssam#endif /* NO_MAIN_ALLOWED */ 429178354Ssam 430178354Ssam/* 431178354Ssam * main - parse arguments and handle options 432178354Ssam */ 433178354Ssam#ifndef NO_MAIN_ALLOWED 434178354Ssamint 435178354Ssammain( 436178354Ssam int argc, 437178354Ssam char *argv[] 438178354Ssam ) 439178354Ssam{ 440178354Ssam return ntpqmain(argc, argv); 441178354Ssam} 442178354Ssam#endif 443178354Ssam 444178354Ssam#ifndef BUILD_AS_LIB 445178354Ssamint 446178354Ssamntpqmain( 447178354Ssam int argc, 448178354Ssam char *argv[] 449178354Ssam ) 450178354Ssam{ 451178354Ssam u_int ihost; 452178354Ssam int icmd; 453178354Ssam 454178354Ssam 455178354Ssam#ifdef SYS_VXWORKS 456178354Ssam clear_globals(); 457178354Ssam taskPrioritySet(taskIdSelf(), 100 ); 458178354Ssam#endif 459178354Ssam 460178354Ssam delay_time.l_ui = 0; 461178354Ssam delay_time.l_uf = DEFDELAY; 462178354Ssam 463178354Ssam init_lib(); /* sets up ipv4_works, ipv6_works */ 464178354Ssam ssl_applink(); 465178354Ssam init_auth(); 466178354Ssam 467178354Ssam /* Check to see if we have IPv6. Otherwise default to IPv4 */ 468178354Ssam if (!ipv6_works) 469178354Ssam ai_fam_default = AF_INET; 470178354Ssam 471178354Ssam /* Fixup keytype's help based on available digest names */ 472178354Ssam 473178354Ssam { 474178354Ssam char *list; 475178354Ssam char *msg; 476178354Ssam 477178354Ssam list = list_digest_names(); 478192468Ssam for (icmd = 0; icmd < sizeof(builtins)/sizeof(builtins[0]); icmd++) { 479178354Ssam if (strcmp("keytype", builtins[icmd].keyword) == 0) 480178354Ssam break; 481178354Ssam } 482178354Ssam 483178354Ssam /* CID: 1295478 */ 484178354Ssam /* This should only "trip" if "keytype" is removed from builtins */ 485178354Ssam INSIST(icmd < sizeof(builtins)/sizeof(builtins[0])); 486178354Ssam 487203422Srpaulo#ifdef OPENSSL 488178354Ssam builtins[icmd].desc[0] = "digest-name"; 489178354Ssam my_easprintf(&msg, 490178354Ssam "set key type to use for authenticated requests, one of:%s", 491178354Ssam list); 492183247Ssam#else 493178354Ssam builtins[icmd].desc[0] = "md5"; 494178354Ssam my_easprintf(&msg, 495183247Ssam "set key type to use for authenticated requests (%s)", 496183247Ssam list); 497183247Ssam#endif 498183247Ssam builtins[icmd].comment = msg; 499183247Ssam free(list); 500178354Ssam } 501178354Ssam 502178354Ssam progname = argv[0]; 503178354Ssam 504178354Ssam { 505178354Ssam int optct = ntpOptionProcess(&ntpqOptions, argc, argv); 506178354Ssam argc -= optct; 507178354Ssam argv += optct; 508178354Ssam } 509178354Ssam 510178354Ssam /* 511178354Ssam * Process options other than -c and -p, which are specially 512178354Ssam * handled by ntpq_custom_opt_handler(). 513178354Ssam */ 514178354Ssam 515178354Ssam debug = OPT_VALUE_SET_DEBUG_LEVEL; 516178354Ssam 517178354Ssam if (HAVE_OPT(IPV4)) 518178354Ssam ai_fam_templ = AF_INET; 519178354Ssam else if (HAVE_OPT(IPV6)) 520178354Ssam ai_fam_templ = AF_INET6; 521178354Ssam else 522178354Ssam ai_fam_templ = ai_fam_default; 523178354Ssam 524178354Ssam if (HAVE_OPT(INTERACTIVE)) 525178354Ssam interactive = 1; 526178354Ssam 527178354Ssam if (HAVE_OPT(NUMERIC)) 528178354Ssam showhostnames = 0; 529178354Ssam 530178354Ssam if (HAVE_OPT(WIDE)) 531178354Ssam wideremote = 1; 532191547Ssam 533191547Ssam old_rv = HAVE_OPT(OLD_RV); 534178354Ssam 535178354Ssam if (0 == argc) { 536178354Ssam ADDHOST(DEFHOST); 537178354Ssam } else { 538178354Ssam for (ihost = 0; ihost < (u_int)argc; ihost++) { 539178354Ssam if ('-' == *argv[ihost]) { 540178354Ssam // 541178354Ssam // If I really cared I'd also check: 542178354Ssam // 0 == argv[ihost][2] 543178354Ssam // 544178354Ssam // and there are other cases as well... 545178354Ssam // 546178354Ssam if ('4' == argv[ihost][1]) { 547178354Ssam ai_fam_templ = AF_INET; 548178354Ssam continue; 549178354Ssam } else if ('6' == argv[ihost][1]) { 550178354Ssam ai_fam_templ = AF_INET6; 551178354Ssam continue; 552178354Ssam } else { 553178354Ssam // XXX Throw a usage error 554178354Ssam } 555178354Ssam } 556178354Ssam ADDHOST(argv[ihost]); 557178354Ssam } 558178354Ssam } 559178354Ssam 560178354Ssam if (numcmds == 0 && interactive == 0 561178354Ssam && isatty(fileno(stdin)) && isatty(fileno(stderr))) { 562178354Ssam interactive = 1; 563178354Ssam } 564178354Ssam 565178354Ssam set_ctrl_c_hook(on_ctrlc); 566178354Ssam#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */ 567178354Ssam if (interactive) 568178354Ssam push_ctrl_c_handler(abortcmd); 569178354Ssam#endif /* SYS_WINNT */ 570178354Ssam 571178354Ssam if (numcmds == 0) { 572192468Ssam (void) openhost(chosts[0].name, chosts[0].fam); 573178354Ssam getcmds(); 574178354Ssam } else { 575178354Ssam for (ihost = 0; ihost < numhosts; ihost++) { 576178354Ssam if (openhost(chosts[ihost].name, chosts[ihost].fam)) 577178354Ssam for (icmd = 0; icmd < numcmds; icmd++) 578178354Ssam docmd(ccmds[icmd]); 579221418Sadrian } 580178354Ssam } 581178354Ssam#ifdef SYS_WINNT 582178354Ssam WSACleanup(); 583178354Ssam#endif /* SYS_WINNT */ 584178354Ssam return 0; 585178354Ssam} 586178354Ssam#endif /* !BUILD_AS_LIB */ 587178354Ssam 588178354Ssam/* 589178354Ssam * openhost - open a socket to a host 590178354Ssam */ 591178354Ssamstatic int 592178354Ssamopenhost( 593178354Ssam const char *hname, 594178354Ssam int fam 595178354Ssam ) 596178354Ssam{ 597178354Ssam const char svc[] = "ntp"; 598178354Ssam char temphost[LENHOSTNAME]; 599178354Ssam int a_info, i; 600178354Ssam struct addrinfo hints, *ai; 601178354Ssam sockaddr_u addr; 602178354Ssam size_t octets; 603178354Ssam register const char *cp; 604178354Ssam char name[LENHOSTNAME]; 605178354Ssam 606178354Ssam /* 607178354Ssam * We need to get by the [] if they were entered 608178354Ssam */ 609178354Ssam 610178354Ssam cp = hname; 611178354Ssam 612178354Ssam if (*cp == '[') { 613178354Ssam cp++; 614178354Ssam for (i = 0; *cp && *cp != ']'; cp++, i++) 615178354Ssam name[i] = *cp; 616178354Ssam if (*cp == ']') { 617178354Ssam name[i] = '\0'; 618178354Ssam hname = name; 619178354Ssam } else { 620178354Ssam return 0; 621178354Ssam } 622178354Ssam } 623178354Ssam 624178354Ssam /* 625178354Ssam * First try to resolve it as an ip address and if that fails, 626178354Ssam * do a fullblown (dns) lookup. That way we only use the dns 627178354Ssam * when it is needed and work around some implementations that 628178354Ssam * will return an "IPv4-mapped IPv6 address" address if you 629178354Ssam * give it an IPv4 address to lookup. 630178354Ssam */ 631178354Ssam ZERO(hints); 632178354Ssam hints.ai_family = fam; 633178354Ssam hints.ai_protocol = IPPROTO_UDP; 634178354Ssam hints.ai_socktype = SOCK_DGRAM; 635178354Ssam hints.ai_flags = Z_AI_NUMERICHOST; 636178354Ssam ai = NULL; 637178354Ssam 638178354Ssam a_info = getaddrinfo(hname, svc, &hints, &ai); 639178354Ssam if (a_info == EAI_NONAME 640178354Ssam#ifdef EAI_NODATA 641178354Ssam || a_info == EAI_NODATA 642178354Ssam#endif 643178354Ssam ) { 644178354Ssam hints.ai_flags = AI_CANONNAME; 645178354Ssam#ifdef AI_ADDRCONFIG 646178354Ssam hints.ai_flags |= AI_ADDRCONFIG; 647178354Ssam#endif 648178354Ssam a_info = getaddrinfo(hname, svc, &hints, &ai); 649178354Ssam } 650178354Ssam#ifdef AI_ADDRCONFIG 651178354Ssam /* Some older implementations don't like AI_ADDRCONFIG. */ 652178354Ssam if (a_info == EAI_BADFLAGS) { 653241138Sadrian hints.ai_flags &= ~AI_ADDRCONFIG; 654178354Ssam a_info = getaddrinfo(hname, svc, &hints, &ai); 655178354Ssam } 656178354Ssam#endif 657178354Ssam if (a_info != 0) { 658178354Ssam fprintf(stderr, "%s\n", gai_strerror(a_info)); 659178354Ssam return 0; 660178354Ssam } 661178354Ssam 662178354Ssam INSIST(ai != NULL); 663178354Ssam ZERO(addr); 664178354Ssam octets = min(sizeof(addr), ai->ai_addrlen); 665178354Ssam memcpy(&addr, ai->ai_addr, octets); 666178354Ssam 667178354Ssam if (ai->ai_canonname == NULL) { 668178354Ssam strlcpy(temphost, stoa(&addr), sizeof(temphost)); 669178354Ssam currenthostisnum = TRUE; 670178354Ssam } else { 671178354Ssam strlcpy(temphost, ai->ai_canonname, sizeof(temphost)); 672178354Ssam currenthostisnum = FALSE; 673178354Ssam } 674178354Ssam 675178354Ssam if (debug > 2) 676183247Ssam printf("Opening host %s (%s)\n", 677183247Ssam temphost, 678178354Ssam (ai->ai_family == AF_INET) 679178354Ssam ? "AF_INET" 680178354Ssam : (ai->ai_family == AF_INET6) 681183247Ssam ? "AF_INET6" 682178354Ssam : "AF-???" 683178354Ssam ); 684178354Ssam 685178354Ssam if (havehost == 1) { 686178354Ssam if (debug > 2) 687178354Ssam printf("Closing old host %s\n", currenthost); 688178354Ssam closesocket(sockfd); 689178354Ssam havehost = 0; 690178354Ssam } 691178354Ssam strlcpy(currenthost, temphost, sizeof(currenthost)); 692178354Ssam 693178354Ssam /* port maps to the same location in both families */ 694178354Ssam s_port = NSRCPORT(&addr); 695178354Ssam#ifdef SYS_VXWORKS 696178354Ssam ((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM); 697178354Ssam if (ai->ai_family == AF_INET) 698178354Ssam *(struct sockaddr_in *)&hostaddr= 699178354Ssam *((struct sockaddr_in *)ai->ai_addr); 700178354Ssam else 701178354Ssam *(struct sockaddr_in6 *)&hostaddr= 702178354Ssam *((struct sockaddr_in6 *)ai->ai_addr); 703178354Ssam#endif /* SYS_VXWORKS */ 704178354Ssam 705178354Ssam#ifdef SYS_WINNT 706178354Ssam { 707178354Ssam int optionValue = SO_SYNCHRONOUS_NONALERT; 708178354Ssam int err; 709178354Ssam 710178354Ssam err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, 711178354Ssam (char *)&optionValue, sizeof(optionValue)); 712178354Ssam if (err) { 713178354Ssam mfprintf(stderr, 714178354Ssam "setsockopt(SO_SYNCHRONOUS_NONALERT)" 715178354Ssam " error: %m\n"); 716178354Ssam freeaddrinfo(ai); 717178354Ssam exit(1); 718178354Ssam } 719178354Ssam } 720178354Ssam#endif /* SYS_WINNT */ 721178354Ssam 722178354Ssam sockfd = socket(ai->ai_family, ai->ai_socktype, 723178354Ssam ai->ai_protocol); 724178354Ssam if (sockfd == INVALID_SOCKET) { 725178354Ssam error("socket"); 726178354Ssam freeaddrinfo(ai); 727178354Ssam return 0; 728178354Ssam } 729178354Ssam 730178354Ssam 731178354Ssam#ifdef NEED_RCVBUF_SLOP 732178354Ssam# ifdef SO_RCVBUF 733178354Ssam { int rbufsize = DATASIZE + 2048; /* 2K for slop */ 734178354Ssam if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, 735178354Ssam &rbufsize, sizeof(int)) == -1) 736178354Ssam error("setsockopt"); 737178354Ssam } 738178354Ssam# endif 739178354Ssam#endif 740178354Ssam 741178354Ssam if 742178354Ssam#ifdef SYS_VXWORKS 743178354Ssam (connect(sockfd, (struct sockaddr *)&hostaddr, 744178354Ssam sizeof(hostaddr)) == -1) 745178354Ssam#else 746178354Ssam (connect(sockfd, (struct sockaddr *)ai->ai_addr, 747178354Ssam ai->ai_addrlen) == -1) 748178354Ssam#endif /* SYS_VXWORKS */ 749178354Ssam { 750178354Ssam error("connect"); 751178354Ssam freeaddrinfo(ai); 752178354Ssam return 0; 753192468Ssam } 754192468Ssam freeaddrinfo(ai); 755178354Ssam havehost = 1; 756178354Ssam numassoc = 0; 757178354Ssam 758178354Ssam return 1; 759178354Ssam} 760178354Ssam 761178354Ssam 762178354Ssamstatic void 763178354Ssamdump_hex_printable( 764178354Ssam const void * data, 765178354Ssam size_t len 766178354Ssam ) 767178354Ssam{ 768178354Ssam const char * cdata; 769178354Ssam const char * rowstart; 770178354Ssam size_t idx; 771178354Ssam size_t rowlen; 772178354Ssam u_char uch; 773178354Ssam 774178354Ssam cdata = data; 775178354Ssam while (len > 0) { 776178354Ssam rowstart = cdata; 777178354Ssam rowlen = min(16, len); 778178354Ssam for (idx = 0; idx < rowlen; idx++) { 779178354Ssam uch = *(cdata++); 780178354Ssam printf("%02x ", uch); 781178354Ssam } 782178354Ssam for ( ; idx < 16 ; idx++) 783178354Ssam printf(" "); 784178354Ssam cdata = rowstart; 785178354Ssam for (idx = 0; idx < rowlen; idx++) { 786178354Ssam uch = *(cdata++); 787178354Ssam printf("%c", (isprint(uch)) 788178354Ssam ? uch 789178354Ssam : '.'); 790178354Ssam } 791178354Ssam printf("\n"); 792178354Ssam len -= rowlen; 793178354Ssam } 794178354Ssam} 795178354Ssam 796178354Ssam 797178354Ssam/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */ 798178354Ssam/* 799178354Ssam * sendpkt - send a packet to the remote host 800178354Ssam */ 801178354Ssamstatic int 802178354Ssamsendpkt( 803178354Ssam void * xdata, 804178354Ssam size_t xdatalen 805178354Ssam ) 806178354Ssam{ 807178354Ssam if (debug >= 3) 808178354Ssam printf("Sending %zu octets\n", xdatalen); 809178354Ssam 810178354Ssam if (send(sockfd, xdata, xdatalen, 0) == -1) { 811178354Ssam warning("write to %s failed", currenthost); 812190391Ssam return -1; 813190391Ssam } 814190391Ssam 815190391Ssam if (debug >= 4) { 816178354Ssam printf("Request packet:\n"); 817190391Ssam dump_hex_printable(xdata, xdatalen); 818178354Ssam } 819178354Ssam return 0; 820178354Ssam} 821178354Ssam 822178354Ssam/* 823178354Ssam * getresponse - get a (series of) response packet(s) and return the data 824178354Ssam */ 825178354Ssamstatic int 826178354Ssamgetresponse( 827178354Ssam int opcode, 828178354Ssam int associd, 829178354Ssam u_short *rstatus, 830178354Ssam size_t *rsize, 831178354Ssam const char **rdata, 832178354Ssam int timeo 833178354Ssam ) 834178354Ssam{ 835178354Ssam struct ntp_control rpkt; 836178354Ssam struct sock_timeval tvo; 837178354Ssam u_short offsets[MAXFRAGS+1]; 838178354Ssam u_short counts[MAXFRAGS+1]; 839178354Ssam u_short offset; 840178354Ssam u_short count; 841178354Ssam size_t numfrags; 842178354Ssam size_t f; 843178354Ssam size_t ff; 844178354Ssam int seenlastfrag; 845178354Ssam int shouldbesize; 846178354Ssam fd_set fds; 847178354Ssam int n; 848178354Ssam int errcode; 849178354Ssam /* absolute timeout checks. Not 'time_t' by intention! */ 850178354Ssam uint32_t tobase; /* base value for timeout */ 851178354Ssam uint32_t tospan; /* timeout span (max delay) */ 852178354Ssam uint32_t todiff; /* current delay */ 853178354Ssam 854178354Ssam /* 855178354Ssam * This is pretty tricky. We may get between 1 and MAXFRAG packets 856178354Ssam * back in response to the request. We peel the data out of 857178354Ssam * each packet and collect it in one long block. When the last 858178354Ssam * packet in the sequence is received we'll know how much data we 859178354Ssam * should have had. Note we use one long time out, should reconsider. 860178354Ssam */ 861178354Ssam *rsize = 0; 862178354Ssam if (rstatus) 863178354Ssam *rstatus = 0; 864178354Ssam *rdata = (char *)pktdata; 865178354Ssam 866178354Ssam numfrags = 0; 867178354Ssam seenlastfrag = 0; 868178354Ssam 869178354Ssam tobase = (uint32_t)time(NULL); 870178354Ssam 871178354Ssam FD_ZERO(&fds); 872178354Ssam 873178354Ssam /* 874178354Ssam * Loop until we have an error or a complete response. Nearly all 875178354Ssam * code paths to loop again use continue. 876178354Ssam */ 877178354Ssam for (;;) { 878178354Ssam 879178354Ssam if (numfrags == 0) 880178354Ssam tvo = tvout; 881178354Ssam else 882178354Ssam tvo = tvsout; 883178354Ssam tospan = (uint32_t)tvo.tv_sec + (tvo.tv_usec != 0); 884178354Ssam 885178354Ssam FD_SET(sockfd, &fds); 886178354Ssam n = select(sockfd+1, &fds, NULL, NULL, &tvo); 887178354Ssam if (n == -1) { 888205791Srpaulo#if !defined(SYS_WINNT) && defined(EINTR) 889205791Srpaulo /* Windows does not know about EINTR (until very 890205791Srpaulo * recently) and the handling of console events 891205791Srpaulo * is *very* different from POSIX/UNIX signal 892205791Srpaulo * handling anyway. 893205516Srpaulo * 894205516Srpaulo * Under non-windows targets we map EINTR as 895205516Srpaulo * 'last packet was received' and try to exit 896192468Ssam * the receive sequence. 897191534Ssam */ 898178354Ssam if (errno == EINTR) { 899178354Ssam seenlastfrag = 1; 900178354Ssam goto maybe_final; 901178354Ssam } 902191546Ssam#endif 903178354Ssam warning("select fails"); 904178354Ssam return -1; 905178354Ssam } 906178354Ssam 907178354Ssam /* 908178354Ssam * Check if this is already too late. Trash the data and 909178354Ssam * fake a timeout if this is so. 910178354Ssam */ 911178354Ssam todiff = (((uint32_t)time(NULL)) - tobase) & 0x7FFFFFFFu; 912178354Ssam if ((n > 0) && (todiff > tospan)) { 913178354Ssam n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0); 914192765Ssam n = 0; /* faked timeout return from 'select()'*/ 915192468Ssam } 916178354Ssam 917178354Ssam if (n == 0) { 918178354Ssam /* 919178354Ssam * Timed out. Return what we have 920178354Ssam */ 921178354Ssam if (numfrags == 0) { 922178354Ssam if (timeo) 923192468Ssam fprintf(stderr, 924178354Ssam "%s: timed out, nothing received\n", 925178354Ssam currenthost); 926178354Ssam return ERR_TIMEOUT; 927178354Ssam } 928178354Ssam if (timeo) 929178354Ssam fprintf(stderr, 930178354Ssam "%s: timed out with incomplete data\n", 931178354Ssam currenthost); 932178354Ssam if (debug) { 933178354Ssam fprintf(stderr, 934178354Ssam "ERR_INCOMPLETE: Received fragments:\n"); 935178354Ssam for (f = 0; f < numfrags; f++) 936178354Ssam fprintf(stderr, 937178354Ssam "%2u: %5d %5d\t%3d octets\n", 938178354Ssam (u_int)f, offsets[f], 939178354Ssam offsets[f] + 940186302Ssam counts[f], 941178354Ssam counts[f]); 942178354Ssam fprintf(stderr, 943178354Ssam "last fragment %sreceived\n", 944178354Ssam (seenlastfrag) 945178354Ssam ? "" 946178354Ssam : "not "); 947178354Ssam } 948178354Ssam return ERR_INCOMPLETE; 949178354Ssam } 950178354Ssam 951178354Ssam n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0); 952178354Ssam if (n == -1) { 953178354Ssam warning("read"); 954178354Ssam return -1; 955178354Ssam } 956178354Ssam 957178354Ssam if (debug >= 4) { 958178354Ssam printf("Response packet:\n"); 959178354Ssam dump_hex_printable(&rpkt, n); 960178354Ssam } 961178354Ssam 962178354Ssam /* 963178354Ssam * Check for format errors. Bug proofing. 964178354Ssam */ 965178354Ssam if (n < (int)CTL_HEADER_LEN) { 966186099Ssam if (debug) 967186151Ssam printf("Short (%d byte) packet received\n", n); 968186099Ssam continue; 969186099Ssam } 970186099Ssam if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION 971178354Ssam || PKT_VERSION(rpkt.li_vn_mode) < NTP_OLDVERSION) { 972178354Ssam if (debug) 973178354Ssam printf("Packet received with version %d\n", 974178354Ssam PKT_VERSION(rpkt.li_vn_mode)); 975178354Ssam continue; 976178354Ssam } 977178354Ssam if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) { 978178354Ssam if (debug) 979178354Ssam printf("Packet received with mode %d\n", 980178354Ssam PKT_MODE(rpkt.li_vn_mode)); 981178354Ssam continue; 982178354Ssam } 983178354Ssam if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) { 984178354Ssam if (debug) 985178354Ssam printf("Received request packet, wanted response\n"); 986178354Ssam continue; 987178354Ssam } 988178354Ssam 989178354Ssam /* 990178354Ssam * Check opcode and sequence number for a match. 991178354Ssam * Could be old data getting to us. 992178354Ssam */ 993178354Ssam if (ntohs(rpkt.sequence) != sequence) { 994178354Ssam if (debug) 995178354Ssam printf("Received sequnce number %d, wanted %d\n", 996178354Ssam ntohs(rpkt.sequence), sequence); 997178354Ssam continue; 998178354Ssam } 999178354Ssam if (CTL_OP(rpkt.r_m_e_op) != opcode) { 1000178354Ssam if (debug) 1001192468Ssam printf( 1002178354Ssam "Received opcode %d, wanted %d (sequence number okay)\n", 1003178354Ssam CTL_OP(rpkt.r_m_e_op), opcode); 1004178354Ssam continue; 1005178354Ssam } 1006178354Ssam 1007178354Ssam /* 1008178354Ssam * Check the error code. If non-zero, return it. 1009178354Ssam */ 1010178354Ssam if (CTL_ISERROR(rpkt.r_m_e_op)) { 1011178354Ssam errcode = (ntohs(rpkt.status) >> 8) & 0xff; 1012178354Ssam if (CTL_ISMORE(rpkt.r_m_e_op)) 1013178354Ssam TRACE(1, ("Error code %d received on not-final packet\n", 1014178354Ssam errcode)); 1015178354Ssam if (errcode == CERR_UNSPEC) 1016178354Ssam return ERR_UNSPEC; 1017178354Ssam return errcode; 1018178354Ssam } 1019178354Ssam 1020178354Ssam /* 1021178354Ssam * Check the association ID to make sure it matches what 1022178354Ssam * we sent. 1023178354Ssam */ 1024178354Ssam if (ntohs(rpkt.associd) != associd) { 1025178354Ssam TRACE(1, ("Association ID %d doesn't match expected %d\n", 1026178354Ssam ntohs(rpkt.associd), associd)); 1027178354Ssam /* 1028178354Ssam * Hack for silly fuzzballs which, at the time of writing, 1029178354Ssam * return an assID of sys.peer when queried for system variables. 1030178354Ssam */ 1031178354Ssam#ifdef notdef 1032178354Ssam continue; 1033178354Ssam#endif 1034178354Ssam } 1035178354Ssam 1036178354Ssam /* 1037178354Ssam * Collect offset and count. Make sure they make sense. 1038178354Ssam */ 1039178354Ssam offset = ntohs(rpkt.offset); 1040178354Ssam count = ntohs(rpkt.count); 1041178354Ssam 1042178354Ssam /* 1043178354Ssam * validate received payload size is padded to next 32-bit 1044178354Ssam * boundary and no smaller than claimed by rpkt.count 1045178354Ssam */ 1046178354Ssam if (n & 0x3) { 1047178354Ssam TRACE(1, ("Response packet not padded, size = %d\n", 1048178354Ssam n)); 1049178354Ssam continue; 1050178354Ssam } 1051178354Ssam 1052178354Ssam shouldbesize = (CTL_HEADER_LEN + count + 3) & ~3; 1053178354Ssam 1054178354Ssam if (n < shouldbesize) { 1055178354Ssam printf("Response packet claims %u octets payload, above %ld received\n", 1056178354Ssam count, (long)n - CTL_HEADER_LEN); 1057178354Ssam return ERR_INCOMPLETE; 1058178354Ssam } 1059178354Ssam 1060178354Ssam if (debug >= 3 && shouldbesize > n) { 1061178354Ssam u_int32 key; 1062178354Ssam u_int32 *lpkt; 1063178354Ssam int maclen; 1064178354Ssam 1065178354Ssam /* 1066178354Ssam * Usually we ignore authentication, but for debugging purposes 1067178354Ssam * we watch it here. 1068178354Ssam */ 1069178354Ssam /* round to 8 octet boundary */ 1070178354Ssam shouldbesize = (shouldbesize + 7) & ~7; 1071178354Ssam 1072178354Ssam maclen = n - shouldbesize; 1073178354Ssam if (maclen >= (int)MIN_MAC_LEN) { 1074178354Ssam printf( 1075178354Ssam "Packet shows signs of authentication (total %d, data %d, mac %d)\n", 1076178354Ssam n, shouldbesize, maclen); 1077178354Ssam lpkt = (u_int32 *)&rpkt; 1078178354Ssam printf("%08lx %08lx %08lx %08lx %08lx %08lx\n", 1079178354Ssam (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 3]), 1080178354Ssam (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 2]), 1081178354Ssam (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 1]), 1082178354Ssam (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32)]), 1083178354Ssam (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 1]), 1084178354Ssam (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 2])); 1085178354Ssam key = ntohl(lpkt[(n - maclen) / sizeof(u_int32)]); 1086178354Ssam printf("Authenticated with keyid %lu\n", (u_long)key); 1087178354Ssam if (key != 0 && key != info_auth_keyid) { 1088178354Ssam printf("We don't know that key\n"); 1089178354Ssam } else { 1090178354Ssam if (authdecrypt(key, (u_int32 *)&rpkt, 1091178354Ssam n - maclen, maclen)) { 1092178354Ssam printf("Auth okay!\n"); 1093178354Ssam } else { 1094178354Ssam printf("Auth failed!\n"); 1095178354Ssam } 1096178354Ssam } 1097186099Ssam } 1098186099Ssam } 1099186099Ssam 1100186099Ssam TRACE(2, ("Got packet, size = %d\n", n)); 1101186099Ssam if (count > (n - CTL_HEADER_LEN)) { 1102178354Ssam TRACE(1, ("Received count of %u octets, data in packet is %ld\n", 1103192468Ssam count, (long)n - CTL_HEADER_LEN)); 1104178354Ssam continue; 1105178354Ssam } 1106178354Ssam if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) { 1107178354Ssam TRACE(1, ("Received count of 0 in non-final fragment\n")); 1108178354Ssam continue; 1109178354Ssam } 1110178354Ssam if (offset + count > sizeof(pktdata)) { 1111178354Ssam TRACE(1, ("Offset %u, count %u, too big for buffer\n", 1112178354Ssam offset, count)); 1113178354Ssam return ERR_TOOMUCH; 1114178354Ssam } 1115178354Ssam if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) { 1116178354Ssam TRACE(1, ("Received second last fragment packet\n")); 1117178354Ssam continue; 1118178354Ssam } 1119178354Ssam 1120178354Ssam /* 1121178354Ssam * So far, so good. Record this fragment, making sure it doesn't 1122178354Ssam * overlap anything. 1123178354Ssam */ 1124178354Ssam TRACE(2, ("Packet okay\n")); 1125178354Ssam 1126178354Ssam if (numfrags > (MAXFRAGS - 1)) { 1127178354Ssam TRACE(2, ("Number of fragments exceeds maximum %d\n", 1128178354Ssam MAXFRAGS - 1)); 1129178354Ssam return ERR_TOOMUCH; 1130178354Ssam } 1131178354Ssam 1132178354Ssam /* 1133178354Ssam * Find the position for the fragment relative to any 1134178354Ssam * previously received. 1135178354Ssam */ 1136178354Ssam for (f = 0; 1137178354Ssam f < numfrags && offsets[f] < offset; 1138178354Ssam f++) { 1139178354Ssam /* empty body */ ; 1140178354Ssam } 1141178354Ssam 1142178354Ssam if (f < numfrags && offset == offsets[f]) { 1143178354Ssam TRACE(1, ("duplicate %u octets at %u ignored, prior %u at %u\n", 1144178354Ssam count, offset, counts[f], offsets[f])); 1145178354Ssam continue; 1146178354Ssam } 1147178354Ssam 1148178354Ssam if (f > 0 && (offsets[f-1] + counts[f-1]) > offset) { 1149178354Ssam TRACE(1, ("received frag at %u overlaps with %u octet frag at %u\n", 1150178354Ssam offset, counts[f-1], offsets[f-1])); 1151178354Ssam continue; 1152178354Ssam } 1153178354Ssam 1154178354Ssam if (f < numfrags && (offset + count) > offsets[f]) { 1155178354Ssam TRACE(1, ("received %u octet frag at %u overlaps with frag at %u\n", 1156178354Ssam count, offset, offsets[f])); 1157178354Ssam continue; 1158178354Ssam } 1159178354Ssam 1160178354Ssam for (ff = numfrags; ff > f; ff--) { 1161178354Ssam offsets[ff] = offsets[ff-1]; 1162178354Ssam counts[ff] = counts[ff-1]; 1163178354Ssam } 1164178354Ssam offsets[f] = offset; 1165178354Ssam counts[f] = count; 1166178354Ssam numfrags++; 1167178354Ssam 1168178354Ssam /* 1169178354Ssam * Got that stuffed in right. Figure out if this was the last. 1170178354Ssam * Record status info out of the last packet. 1171178354Ssam */ 1172178354Ssam if (!CTL_ISMORE(rpkt.r_m_e_op)) { 1173178354Ssam seenlastfrag = 1; 1174178354Ssam if (rstatus != 0) 1175178354Ssam *rstatus = ntohs(rpkt.status); 1176178354Ssam } 1177178354Ssam 1178178354Ssam /* 1179178354Ssam * Copy the data into the data buffer, and bump the 1180178354Ssam * timout base in case we need more. 1181178354Ssam */ 1182178354Ssam memcpy((char *)pktdata + offset, &rpkt.u, count); 1183178354Ssam tobase = (uint32_t)time(NULL); 1184178354Ssam 1185178354Ssam /* 1186178354Ssam * If we've seen the last fragment, look for holes in the sequence. 1187178354Ssam * If there aren't any, we're done. 1188178354Ssam */ 1189178354Ssam maybe_final: 1190178354Ssam if (seenlastfrag && offsets[0] == 0) { 1191178354Ssam for (f = 1; f < numfrags; f++) 1192178354Ssam if (offsets[f-1] + counts[f-1] != 1193178354Ssam offsets[f]) 1194178354Ssam break; 1195178354Ssam if (f == numfrags) { 1196178354Ssam *rsize = offsets[f-1] + counts[f-1]; 1197178354Ssam TRACE(1, ("%lu packets reassembled into response\n", 1198178354Ssam (u_long)numfrags)); 1199178354Ssam return 0; 1200178354Ssam } 1201178354Ssam } 1202178354Ssam } /* giant for (;;) collecting response packets */ 1203178354Ssam} /* getresponse() */ 1204178354Ssam 1205178354Ssam 1206178354Ssam/* 1207178354Ssam * sendrequest - format and send a request packet 1208178354Ssam */ 1209178354Ssamstatic int 1210178354Ssamsendrequest( 1211178354Ssam int opcode, 1212178354Ssam associd_t associd, 1213178354Ssam int auth, 1214178354Ssam size_t qsize, 1215178354Ssam const char *qdata 1216178354Ssam ) 1217178354Ssam{ 1218178354Ssam struct ntp_control qpkt; 1219178354Ssam size_t pktsize; 1220178354Ssam u_long key_id; 1221178354Ssam char * pass; 1222178354Ssam size_t maclen; 1223178354Ssam 1224178354Ssam /* 1225178354Ssam * Check to make sure the data will fit in one packet 1226178354Ssam */ 1227178354Ssam if (qsize > CTL_MAX_DATA_LEN) { 1228178354Ssam fprintf(stderr, 1229178354Ssam "***Internal error! qsize (%zu) too large\n", 1230178354Ssam qsize); 1231178354Ssam return 1; 1232178354Ssam } 1233178354Ssam 1234178354Ssam /* 1235178354Ssam * Fill in the packet 1236178354Ssam */ 1237178354Ssam qpkt.li_vn_mode = PKT_LI_VN_MODE(0, pktversion, MODE_CONTROL); 1238178354Ssam qpkt.r_m_e_op = (u_char)(opcode & CTL_OP_MASK); 1239178354Ssam qpkt.sequence = htons(sequence); 1240178354Ssam qpkt.status = 0; 1241178354Ssam qpkt.associd = htons((u_short)associd); 1242178354Ssam qpkt.offset = 0; 1243178354Ssam qpkt.count = htons((u_short)qsize); 1244178354Ssam 1245178354Ssam pktsize = CTL_HEADER_LEN; 1246178354Ssam 1247178354Ssam /* 1248178354Ssam * If we have data, copy and pad it out to a 32-bit boundary. 1249178354Ssam */ 1250178354Ssam if (qsize > 0) { 1251178354Ssam memcpy(&qpkt.u, qdata, (size_t)qsize); 1252178354Ssam pktsize += qsize; 1253178354Ssam while (pktsize & (sizeof(u_int32) - 1)) { 1254178354Ssam qpkt.u.data[qsize++] = 0; 1255178354Ssam pktsize++; 1256178354Ssam } 1257178354Ssam } 1258178354Ssam 1259178354Ssam /* 1260178354Ssam * If it isn't authenticated we can just send it. Otherwise 1261178354Ssam * we're going to have to think about it a little. 1262178354Ssam */ 1263200242Srpaulo if (!auth && !always_auth) { 1264178354Ssam return sendpkt(&qpkt, pktsize); 1265178354Ssam } 1266178354Ssam 1267178354Ssam /* 1268178354Ssam * Pad out packet to a multiple of 8 octets to be sure 1269178354Ssam * receiver can handle it. 1270178354Ssam */ 1271178354Ssam while (pktsize & 7) { 1272178354Ssam qpkt.u.data[qsize++] = 0; 1273178354Ssam pktsize++; 1274178354Ssam } 1275178354Ssam 1276178354Ssam /* 1277178354Ssam * Get the keyid and the password if we don't have one. 1278178354Ssam */ 1279178354Ssam if (info_auth_keyid == 0) { 1280178354Ssam key_id = getkeyid("Keyid: "); 1281178354Ssam if (key_id == 0 || key_id > NTP_MAXKEY) { 1282178354Ssam fprintf(stderr, 1283178354Ssam "Invalid key identifier\n"); 1284178354Ssam return 1; 1285178354Ssam } 1286178354Ssam info_auth_keyid = key_id; 1287178354Ssam } 1288178354Ssam if (!authistrusted(info_auth_keyid)) { 1289178354Ssam pass = getpass_keytype(info_auth_keytype); 1290178354Ssam if ('\0' == pass[0]) { 1291178354Ssam fprintf(stderr, "Invalid password\n"); 1292178354Ssam return 1; 1293178354Ssam } 1294178354Ssam authusekey(info_auth_keyid, info_auth_keytype, 1295178354Ssam (u_char *)pass); 1296178354Ssam authtrust(info_auth_keyid, 1); 1297178354Ssam } 1298178354Ssam 1299178354Ssam /* 1300178354Ssam * Do the encryption. 1301178354Ssam */ 1302178354Ssam maclen = authencrypt(info_auth_keyid, (void *)&qpkt, pktsize); 1303178354Ssam if (!maclen) { 1304178354Ssam fprintf(stderr, "Key not found\n"); 1305178354Ssam return 1; 1306178354Ssam } else if ((size_t)maclen != (info_auth_hashlen + sizeof(keyid_t))) { 1307178354Ssam fprintf(stderr, 1308178354Ssam "%zu octet MAC, %zu expected with %zu octet digest\n", 1309178354Ssam maclen, (info_auth_hashlen + sizeof(keyid_t)), 1310178354Ssam info_auth_hashlen); 1311178354Ssam return 1; 1312178354Ssam } 1313178354Ssam 1314178354Ssam return sendpkt((char *)&qpkt, pktsize + maclen); 1315178354Ssam} 1316178354Ssam 1317178354Ssam 1318178354Ssam/* 1319178354Ssam * show_error_msg - display the error text for a mode 6 error response. 1320178354Ssam */ 1321178354Ssamvoid 1322178354Ssamshow_error_msg( 1323178354Ssam int m6resp, 1324178354Ssam associd_t associd 1325178354Ssam ) 1326178354Ssam{ 1327178354Ssam if (numhosts > 1) 1328178354Ssam fprintf(stderr, "server=%s ", currenthost); 1329178354Ssam 1330178354Ssam switch(m6resp) { 1331178354Ssam 1332178354Ssam case CERR_BADFMT: 1333178354Ssam fprintf(stderr, 1334178354Ssam "***Server reports a bad format request packet\n"); 1335178354Ssam break; 1336178354Ssam 1337178354Ssam case CERR_PERMISSION: 1338178354Ssam fprintf(stderr, 1339178354Ssam "***Server disallowed request (authentication?)\n"); 1340178354Ssam break; 1341178354Ssam 1342178354Ssam case CERR_BADOP: 1343178354Ssam fprintf(stderr, 1344178354Ssam "***Server reports a bad opcode in request\n"); 1345178354Ssam break; 1346178354Ssam 1347178354Ssam case CERR_BADASSOC: 1348178354Ssam fprintf(stderr, 1349178354Ssam "***Association ID %d unknown to server\n", 1350178354Ssam associd); 1351178354Ssam break; 1352178354Ssam 1353178354Ssam case CERR_UNKNOWNVAR: 1354178354Ssam fprintf(stderr, 1355178354Ssam "***A request variable unknown to the server\n"); 1356178354Ssam break; 1357178354Ssam 1358178354Ssam case CERR_BADVALUE: 1359178354Ssam fprintf(stderr, 1360178354Ssam "***Server indicates a request variable was bad\n"); 1361178354Ssam break; 1362178354Ssam 1363178354Ssam case ERR_UNSPEC: 1364178354Ssam fprintf(stderr, 1365178354Ssam "***Server returned an unspecified error\n"); 1366178354Ssam break; 1367178354Ssam 1368178354Ssam case ERR_TIMEOUT: 1369178354Ssam fprintf(stderr, "***Request timed out\n"); 1370178354Ssam break; 1371178354Ssam 1372178354Ssam case ERR_INCOMPLETE: 1373178354Ssam fprintf(stderr, 1374178354Ssam "***Response from server was incomplete\n"); 1375178354Ssam break; 1376178354Ssam 1377178354Ssam case ERR_TOOMUCH: 1378178354Ssam fprintf(stderr, 1379178354Ssam "***Buffer size exceeded for returned data\n"); 1380178354Ssam break; 1381178354Ssam 1382178354Ssam default: 1383178354Ssam fprintf(stderr, 1384178354Ssam "***Server returns unknown error code %d\n", 1385178354Ssam m6resp); 1386178354Ssam } 1387178354Ssam} 1388178354Ssam 1389178354Ssam/* 1390178354Ssam * doquery - send a request and process the response, displaying 1391178354Ssam * error messages for any error responses. 1392178354Ssam */ 1393178354Ssamint 1394178354Ssamdoquery( 1395178354Ssam int opcode, 1396178354Ssam associd_t associd, 1397178354Ssam int auth, 1398178354Ssam size_t qsize, 1399178354Ssam const char *qdata, 1400178354Ssam u_short *rstatus, 1401178354Ssam size_t *rsize, 1402178354Ssam const char **rdata 1403178354Ssam ) 1404178354Ssam{ 1405178354Ssam return doqueryex(opcode, associd, auth, qsize, qdata, rstatus, 1406178354Ssam rsize, rdata, FALSE); 1407178354Ssam} 1408178354Ssam 1409178354Ssam 1410178354Ssam/* 1411178354Ssam * doqueryex - send a request and process the response, optionally 1412178354Ssam * displaying error messages for any error responses. 1413178354Ssam */ 1414178354Ssamint 1415178354Ssamdoqueryex( 1416178354Ssam int opcode, 1417178354Ssam associd_t associd, 1418178354Ssam int auth, 1419178354Ssam size_t qsize, 1420178354Ssam const char *qdata, 1421178354Ssam u_short *rstatus, 1422178354Ssam size_t *rsize, 1423178354Ssam const char **rdata, 1424178354Ssam int quiet 1425178354Ssam ) 1426178354Ssam{ 1427178354Ssam int res; 1428178354Ssam int done; 1429178354Ssam 1430178354Ssam /* 1431178354Ssam * Check to make sure host is open 1432178354Ssam */ 1433178354Ssam if (!havehost) { 1434178354Ssam fprintf(stderr, "***No host open, use `host' command\n"); 1435178354Ssam return -1; 1436178354Ssam } 1437178354Ssam 1438178354Ssam done = 0; 1439178354Ssam sequence++; 1440178354Ssam 1441178354Ssam again: 1442178354Ssam /* 1443178354Ssam * send a request 1444178354Ssam */ 1445178354Ssam res = sendrequest(opcode, associd, auth, qsize, qdata); 1446178354Ssam if (res != 0) 1447178354Ssam return res; 1448178354Ssam 1449178354Ssam /* 1450178354Ssam * Get the response. If we got a standard error, print a message 1451178354Ssam */ 1452178354Ssam res = getresponse(opcode, associd, rstatus, rsize, rdata, done); 1453178354Ssam 1454178354Ssam if (res > 0) { 1455178354Ssam if (!done && (res == ERR_TIMEOUT || res == ERR_INCOMPLETE)) { 1456178354Ssam if (res == ERR_INCOMPLETE) { 1457178354Ssam /* 1458178354Ssam * better bump the sequence so we don't 1459178354Ssam * get confused about differing fragments. 1460178354Ssam */ 1461178354Ssam sequence++; 1462178354Ssam } 1463178354Ssam done = 1; 1464178354Ssam goto again; 1465178354Ssam } 1466178354Ssam if (!quiet) 1467178354Ssam show_error_msg(res, associd); 1468178354Ssam 1469178354Ssam } 1470178354Ssam return res; 1471178354Ssam} 1472178354Ssam 1473178354Ssam 1474178354Ssam#ifndef BUILD_AS_LIB 1475178354Ssam/* 1476178354Ssam * getcmds - read commands from the standard input and execute them 1477178354Ssam */ 1478178354Ssamstatic void 1479178354Ssamgetcmds(void) 1480178354Ssam{ 1481178354Ssam char * line; 1482178354Ssam int count; 1483178354Ssam 1484178354Ssam ntp_readline_init(interactive ? prompt : NULL); 1485178354Ssam 1486178354Ssam for (;;) { 1487178354Ssam line = ntp_readline(&count); 1488178354Ssam if (NULL == line) 1489178354Ssam break; 1490178354Ssam docmd(line); 1491178354Ssam free(line); 1492178354Ssam } 1493178354Ssam 1494178354Ssam ntp_readline_uninit(); 1495178354Ssam} 1496178354Ssam#endif /* !BUILD_AS_LIB */ 1497178354Ssam 1498178354Ssam 1499178354Ssam#if !defined(SYS_WINNT) && !defined(BUILD_AS_LIB) 1500178354Ssam/* 1501178354Ssam * abortcmd - catch interrupts and abort the current command 1502178354Ssam */ 1503178354Ssamstatic int 1504178354Ssamabortcmd(void) 1505178354Ssam{ 1506178354Ssam if (current_output == stdout) 1507178354Ssam (void) fflush(stdout); 1508178354Ssam putc('\n', stderr); 1509178354Ssam (void) fflush(stderr); 1510178354Ssam if (jump) { 1511178354Ssam jump = 0; 1512178354Ssam longjmp(interrupt_buf, 1); 1513178354Ssam } 1514178354Ssam return TRUE; 1515178354Ssam} 1516178354Ssam#endif /* !SYS_WINNT && !BUILD_AS_LIB */ 1517178354Ssam 1518178354Ssam 1519178354Ssam#ifndef BUILD_AS_LIB 1520178354Ssam/* 1521178354Ssam * docmd - decode the command line and execute a command 1522178354Ssam */ 1523178354Ssamstatic void 1524178354Ssamdocmd( 1525178354Ssam const char *cmdline 1526178354Ssam ) 1527178354Ssam{ 1528178354Ssam char *tokens[1+MAXARGS+2]; 1529178354Ssam struct parse pcmd; 1530178354Ssam int ntok; 1531178354Ssam static int i; 1532178354Ssam struct xcmd *xcmd; 1533178354Ssam 1534178354Ssam /* 1535178354Ssam * Tokenize the command line. If nothing on it, return. 1536178354Ssam */ 1537178354Ssam tokenize(cmdline, tokens, &ntok); 1538178354Ssam if (ntok == 0) 1539178354Ssam return; 1540178354Ssam 1541178354Ssam /* 1542178354Ssam * Find the appropriate command description. 1543178354Ssam */ 1544178354Ssam i = findcmd(tokens[0], builtins, opcmds, &xcmd); 1545178354Ssam if (i == 0) { 1546178354Ssam (void) fprintf(stderr, "***Command `%s' unknown\n", 1547178354Ssam tokens[0]); 1548178354Ssam return; 1549178354Ssam } else if (i >= 2) { 1550178354Ssam (void) fprintf(stderr, "***Command `%s' ambiguous\n", 1551178354Ssam tokens[0]); 1552178354Ssam return; 1553178354Ssam } 1554178354Ssam 1555178354Ssam /* Warn about ignored extra args */ 1556178354Ssam for (i = MAXARGS + 1; i < ntok ; ++i) { 1557178354Ssam fprintf(stderr, "***Extra arg `%s' ignored\n", tokens[i]); 1558178354Ssam } 1559178354Ssam 1560178354Ssam /* 1561178354Ssam * Save the keyword, then walk through the arguments, interpreting 1562178354Ssam * as we go. 1563178354Ssam */ 1564178354Ssam pcmd.keyword = tokens[0]; 1565178354Ssam pcmd.nargs = 0; 1566178354Ssam for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) { 1567178354Ssam if ((i+1) >= ntok) { 1568178354Ssam if (!(xcmd->arg[i] & OPT)) { 1569178354Ssam printusage(xcmd, stderr); 1570178354Ssam return; 1571178354Ssam } 1572178354Ssam break; 1573178354Ssam } 1574178354Ssam if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>')) 1575178354Ssam break; 1576178354Ssam if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i])) 1577178354Ssam return; 1578178354Ssam pcmd.nargs++; 1579178354Ssam } 1580178354Ssam 1581178354Ssam i++; 1582178354Ssam if (i < ntok && *tokens[i] == '>') { 1583178354Ssam char *fname; 1584178354Ssam 1585178354Ssam if (*(tokens[i]+1) != '\0') 1586178354Ssam fname = tokens[i]+1; 1587178354Ssam else if ((i+1) < ntok) 1588178354Ssam fname = tokens[i+1]; 1589178354Ssam else { 1590178354Ssam (void) fprintf(stderr, "***No file for redirect\n"); 1591178354Ssam return; 1592178354Ssam } 1593178354Ssam 1594178354Ssam current_output = fopen(fname, "w"); 1595178354Ssam if (current_output == NULL) { 1596178354Ssam (void) fprintf(stderr, "***Error opening %s: ", fname); 1597178354Ssam perror(""); 1598178354Ssam return; 1599178354Ssam } 1600178354Ssam i = 1; /* flag we need a close */ 1601178354Ssam } else { 1602178354Ssam current_output = stdout; 1603178354Ssam i = 0; /* flag no close */ 1604178354Ssam } 1605178354Ssam 1606178354Ssam if (interactive && setjmp(interrupt_buf)) { 1607178354Ssam jump = 0; 1608178354Ssam return; 1609178354Ssam } else { 1610178354Ssam jump++; 1611178354Ssam (xcmd->handler)(&pcmd, current_output); 1612178354Ssam jump = 0; /* HMS: 961106: was after fclose() */ 1613178354Ssam if (i) (void) fclose(current_output); 1614178354Ssam } 1615178354Ssam 1616178354Ssam return; 1617178354Ssam} 1618178354Ssam 1619178354Ssam 1620178354Ssam/* 1621178354Ssam * tokenize - turn a command line into tokens 1622178354Ssam * 1623178354Ssam * SK: Modified to allow a quoted string 1624178354Ssam * 1625178354Ssam * HMS: If the first character of the first token is a ':' then (after 1626178354Ssam * eating inter-token whitespace) the 2nd token is the rest of the line. 1627178354Ssam */ 1628178354Ssam 1629178354Ssamstatic void 1630178354Ssamtokenize( 1631178354Ssam const char *line, 1632178354Ssam char **tokens, 1633178354Ssam int *ntok 1634178354Ssam ) 1635178354Ssam{ 1636178354Ssam register const char *cp; 1637178354Ssam register char *sp; 1638178354Ssam static char tspace[MAXLINE]; 1639193543Ssam 1640178354Ssam sp = tspace; 1641178354Ssam cp = line; 1642178354Ssam for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) { 1643178354Ssam tokens[*ntok] = sp; 1644178354Ssam 1645178354Ssam /* Skip inter-token whitespace */ 1646178354Ssam while (ISSPACE(*cp)) 1647178354Ssam cp++; 1648178354Ssam 1649178354Ssam /* If we're at EOL we're done */ 1650178354Ssam if (ISEOL(*cp)) 1651178354Ssam break; 1652178354Ssam 1653178354Ssam /* If this is the 2nd token and the first token begins 1654178354Ssam * with a ':', then just grab to EOL. 1655178354Ssam */ 1656178354Ssam 1657178354Ssam if (*ntok == 1 && tokens[0][0] == ':') { 1658178354Ssam do { 1659178354Ssam if (sp - tspace >= MAXLINE) 1660178354Ssam goto toobig; 1661178354Ssam *sp++ = *cp++; 1662178354Ssam } while (!ISEOL(*cp)); 1663178354Ssam } 1664178354Ssam 1665178354Ssam /* Check if this token begins with a double quote. 1666178354Ssam * If yes, continue reading till the next double quote 1667178354Ssam */ 1668178354Ssam else if (*cp == '\"') { 1669178354Ssam ++cp; 1670178354Ssam do { 1671178354Ssam if (sp - tspace >= MAXLINE) 1672178354Ssam goto toobig; 1673178354Ssam *sp++ = *cp++; 1674178354Ssam } while ((*cp != '\"') && !ISEOL(*cp)); 1675178354Ssam /* HMS: a missing closing " should be an error */ 1676178354Ssam } 1677178354Ssam else { 1678178354Ssam do { 1679178354Ssam if (sp - tspace >= MAXLINE) 1680178354Ssam goto toobig; 1681178354Ssam *sp++ = *cp++; 1682178354Ssam } while ((*cp != '\"') && !ISSPACE(*cp) && !ISEOL(*cp)); 1683178354Ssam /* HMS: Why check for a " in the previous line? */ 1684192468Ssam } 1685178354Ssam 1686178354Ssam if (sp - tspace >= MAXLINE) 1687178354Ssam goto toobig; 1688178354Ssam *sp++ = '\0'; 1689178354Ssam } 1690178354Ssam return; 1691178354Ssam 1692178354Ssam toobig: 1693178354Ssam *ntok = 0; 1694178354Ssam fprintf(stderr, 1695178354Ssam "***Line `%s' is too big\n", 1696178354Ssam line); 1697178354Ssam return; 1698178354Ssam} 1699178354Ssam 1700178354Ssam 1701178354Ssam/* 1702178354Ssam * getarg - interpret an argument token 1703178354Ssam */ 1704178354Ssamstatic int 1705178354Ssamgetarg( 1706178354Ssam const char *str, 1707178354Ssam int code, 1708178354Ssam arg_v *argp 1709178354Ssam ) 1710178354Ssam{ 1711178354Ssam u_long ul; 1712178354Ssam 1713178354Ssam switch (code & ~OPT) { 1714178354Ssam case NTP_STR: 1715178354Ssam argp->string = str; 1716178354Ssam break; 1717178354Ssam 1718178354Ssam case NTP_ADD: 1719178354Ssam if (!getnetnum(str, &argp->netnum, NULL, 0)) 1720178354Ssam return 0; 1721178354Ssam break; 1722178354Ssam 1723178354Ssam case NTP_UINT: 1724178354Ssam if ('&' == str[0]) { 1725178354Ssam if (!atouint(&str[1], &ul)) { 1726178354Ssam fprintf(stderr, 1727178354Ssam "***Association index `%s' invalid/undecodable\n", 1728178354Ssam str); 1729178354Ssam return 0; 1730178354Ssam } 1731178354Ssam if (0 == numassoc) { 1732178354Ssam dogetassoc(stdout); 1733178354Ssam if (0 == numassoc) { 1734178354Ssam fprintf(stderr, 1735178354Ssam "***No associations found, `%s' unknown\n", 1736178354Ssam str); 1737178354Ssam return 0; 1738178354Ssam } 1739192468Ssam } 1740178354Ssam ul = min(ul, numassoc); 1741178354Ssam argp->uval = assoc_cache[ul - 1].assid; 1742178354Ssam break; 1743178354Ssam } 1744178354Ssam if (!atouint(str, &argp->uval)) { 1745178354Ssam fprintf(stderr, "***Illegal unsigned value %s\n", 1746178354Ssam str); 1747178354Ssam return 0; 1748178354Ssam } 1749178354Ssam break; 1750178354Ssam 1751178354Ssam case NTP_INT: 1752178354Ssam if (!atoint(str, &argp->ival)) { 1753178354Ssam fprintf(stderr, "***Illegal integer value %s\n", 1754178354Ssam str); 1755178354Ssam return 0; 1756178354Ssam } 1757178354Ssam break; 1758178354Ssam 1759178354Ssam case IP_VERSION: 1760178354Ssam if (!strcmp("-6", str)) { 1761178354Ssam argp->ival = 6; 1762178354Ssam } else if (!strcmp("-4", str)) { 1763178354Ssam argp->ival = 4; 1764178354Ssam } else { 1765178354Ssam fprintf(stderr, "***Version must be either 4 or 6\n"); 1766178354Ssam return 0; 1767178354Ssam } 1768178354Ssam break; 1769178354Ssam } 1770178354Ssam 1771178354Ssam return 1; 1772178354Ssam} 1773178354Ssam#endif /* !BUILD_AS_LIB */ 1774178354Ssam 1775178354Ssam 1776178354Ssam/* 1777178354Ssam * findcmd - find a command in a command description table 1778178354Ssam */ 1779178354Ssamstatic int 1780178354Ssamfindcmd( 1781178354Ssam const char * str, 1782178354Ssam struct xcmd * clist1, 1783178354Ssam struct xcmd * clist2, 1784178354Ssam struct xcmd ** cmd 1785178354Ssam ) 1786178354Ssam{ 1787178354Ssam struct xcmd *cl; 1788178354Ssam size_t clen; 1789178354Ssam int nmatch; 1790178354Ssam struct xcmd *nearmatch = NULL; 1791178354Ssam struct xcmd *clist; 1792178354Ssam 1793178354Ssam clen = strlen(str); 1794178354Ssam nmatch = 0; 1795178354Ssam if (clist1 != 0) 1796178354Ssam clist = clist1; 1797178354Ssam else if (clist2 != 0) 1798178354Ssam clist = clist2; 1799178354Ssam else 1800178354Ssam return 0; 1801178354Ssam 1802178354Ssam again: 1803228514Sadrian for (cl = clist; cl->keyword != 0; cl++) { 1804228514Sadrian /* do a first character check, for efficiency */ 1805228622Sbschmidt if (*str != *(cl->keyword)) 1806228514Sadrian continue; 1807228514Sadrian if (strncmp(str, cl->keyword, (unsigned)clen) == 0) { 1808228514Sadrian /* 1809228514Sadrian * Could be extact match, could be approximate. 1810228514Sadrian * Is exact if the length of the keyword is the 1811228514Sadrian * same as the str. 1812178354Ssam */ 1813178354Ssam if (*((cl->keyword) + clen) == '\0') { 1814178354Ssam *cmd = cl; 1815178354Ssam return 1; 1816178354Ssam } 1817178354Ssam nmatch++; 1818178354Ssam nearmatch = cl; 1819178354Ssam } 1820178354Ssam } 1821178354Ssam 1822178354Ssam /* 1823178354Ssam * See if there is more to do. If so, go again. Sorry about the 1824178354Ssam * goto, too much looking at BSD sources... 1825178354Ssam */ 1826178354Ssam if (clist == clist1 && clist2 != 0) { 1827178354Ssam clist = clist2; 1828178354Ssam goto again; 1829178354Ssam } 1830178354Ssam 1831178354Ssam /* 1832178354Ssam * If we got extactly 1 near match, use it, else return number 1833178354Ssam * of matches. 1834178354Ssam */ 1835178354Ssam if (nmatch == 1) { 1836178354Ssam *cmd = nearmatch; 1837178354Ssam return 1; 1838178354Ssam } 1839178354Ssam return nmatch; 1840178354Ssam} 1841178354Ssam 1842178354Ssam 1843178354Ssam/* 1844178354Ssam * getnetnum - given a host name, return its net number 1845178354Ssam * and (optional) full name 1846178354Ssam */ 1847178354Ssamint 1848178354Ssamgetnetnum( 1849178354Ssam const char *hname, 1850178354Ssam sockaddr_u *num, 1851178354Ssam char *fullhost, 1852178354Ssam int af 1853178354Ssam ) 1854178354Ssam{ 1855178354Ssam struct addrinfo hints, *ai = NULL; 1856178354Ssam 1857178354Ssam ZERO(hints); 1858178354Ssam hints.ai_flags = AI_CANONNAME; 1859178354Ssam#ifdef AI_ADDRCONFIG 1860178354Ssam hints.ai_flags |= AI_ADDRCONFIG; 1861178354Ssam#endif 1862178354Ssam 1863178354Ssam /* 1864178354Ssam * decodenetnum only works with addresses, but handles syntax 1865178354Ssam * that getaddrinfo doesn't: [2001::1]:1234 1866178354Ssam */ 1867178354Ssam if (decodenetnum(hname, num)) { 1868178354Ssam if (fullhost != NULL) 1869178354Ssam getnameinfo(&num->sa, SOCKLEN(num), fullhost, 1870178354Ssam LENHOSTNAME, NULL, 0, 0); 1871178354Ssam return 1; 1872178354Ssam } else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) { 1873178354Ssam INSIST(sizeof(*num) >= ai->ai_addrlen); 1874178354Ssam memcpy(num, ai->ai_addr, ai->ai_addrlen); 1875178354Ssam if (fullhost != NULL) { 1876178354Ssam if (ai->ai_canonname != NULL) 1877178354Ssam strlcpy(fullhost, ai->ai_canonname, 1878178354Ssam LENHOSTNAME); 1879178354Ssam else 1880178354Ssam getnameinfo(&num->sa, SOCKLEN(num), 1881178354Ssam fullhost, LENHOSTNAME, NULL, 1882178354Ssam 0, 0); 1883178354Ssam } 1884178354Ssam freeaddrinfo(ai); 1885178354Ssam return 1; 1886178354Ssam } 1887178354Ssam fprintf(stderr, "***Can't find host %s\n", hname); 1888178354Ssam 1889178354Ssam return 0; 1890228622Sbschmidt} 1891178354Ssam 1892178354Ssam 1893178354Ssam/* 1894178354Ssam * nntohost - convert network number to host name. This routine enforces 1895178354Ssam * the showhostnames setting. 1896178354Ssam */ 1897178354Ssamconst char * 1898178354Ssamnntohost( 1899178354Ssam sockaddr_u *netnum 1900178354Ssam ) 1901178354Ssam{ 1902178354Ssam return nntohost_col(netnum, LIB_BUFLENGTH - 1, FALSE); 1903178354Ssam} 1904178354Ssam 1905178354Ssam 1906178354Ssam/* 1907178354Ssam * nntohost_col - convert network number to host name in fixed width. 1908178354Ssam * This routine enforces the showhostnames setting. 1909178354Ssam * When displaying hostnames longer than the width, 1910192468Ssam * the first part of the hostname is displayed. When 1911192468Ssam * displaying numeric addresses longer than the width, 1912178354Ssam * Such as IPv6 addresses, the caller decides whether 1913192468Ssam * the first or last of the numeric address is used. 1914178354Ssam */ 1915178354Ssamconst char * 1916178354Ssamnntohost_col( 1917178354Ssam sockaddr_u * addr, 1918178354Ssam size_t width, 1919178354Ssam int preserve_lowaddrbits 1920178354Ssam ) 1921178354Ssam{ 1922178354Ssam const char * out; 1923178354Ssam 1924178354Ssam if (!showhostnames || SOCK_UNSPEC(addr)) { 1925178354Ssam if (preserve_lowaddrbits) 1926178354Ssam out = trunc_left(stoa(addr), width); 1927178354Ssam else 1928178354Ssam out = trunc_right(stoa(addr), width); 1929178354Ssam } else if (ISREFCLOCKADR(addr)) { 1930178354Ssam out = refnumtoa(addr); 1931178354Ssam } else { 1932178354Ssam out = trunc_right(socktohost(addr), width); 1933178354Ssam } 1934178354Ssam return out; 1935178354Ssam} 1936178354Ssam 1937178354Ssam 1938178354Ssam/* 1939178354Ssam * nntohostp() is the same as nntohost() plus a :port suffix 1940178354Ssam */ 1941178354Ssamconst char * 1942178354Ssamnntohostp( 1943178354Ssam sockaddr_u *netnum 1944178354Ssam ) 1945178354Ssam{ 1946178354Ssam const char * hostn; 1947178354Ssam char * buf; 1948178354Ssam 1949178354Ssam if (!showhostnames || SOCK_UNSPEC(netnum)) 1950178354Ssam return sptoa(netnum); 1951178354Ssam else if (ISREFCLOCKADR(netnum)) 1952178354Ssam return refnumtoa(netnum); 1953178354Ssam 1954178354Ssam hostn = socktohost(netnum); 1955178354Ssam LIB_GETBUF(buf); 1956178354Ssam snprintf(buf, LIB_BUFLENGTH, "%s:%u", hostn, SRCPORT(netnum)); 1957178354Ssam 1958178354Ssam return buf; 1959178354Ssam} 1960178354Ssam 1961178354Ssam/* 1962178354Ssam * rtdatetolfp - decode an RT-11 date into an l_fp 1963178354Ssam */ 1964178354Ssamstatic int 1965178354Ssamrtdatetolfp( 1966178354Ssam char *str, 1967178354Ssam l_fp *lfp 1968178354Ssam ) 1969178354Ssam{ 1970178354Ssam register char *cp; 1971178354Ssam register int i; 1972178354Ssam struct calendar cal; 1973178354Ssam char buf[4]; 1974178354Ssam 1975178354Ssam cal.yearday = 0; 1976178354Ssam 1977178354Ssam /* 1978178354Ssam * An RT-11 date looks like: 1979178354Ssam * 1980178354Ssam * d[d]-Mth-y[y] hh:mm:ss 1981178354Ssam * 1982178354Ssam * (No docs, but assume 4-digit years are also legal...) 1983178354Ssam * 1984178354Ssam * d[d]-Mth-y[y[y[y]]] hh:mm:ss 1985178354Ssam */ 1986178354Ssam cp = str; 1987178354Ssam if (!isdigit((int)*cp)) { 1988178354Ssam if (*cp == '-') { 1989178354Ssam /* 1990178354Ssam * Catch special case 1991178354Ssam */ 1992178354Ssam L_CLR(lfp); 1993178354Ssam return 1; 1994178354Ssam } 1995178354Ssam return 0; 1996178354Ssam } 1997178354Ssam 1998178354Ssam cal.monthday = (u_char) (*cp++ - '0'); /* ascii dependent */ 1999178354Ssam if (isdigit((int)*cp)) { 2000178354Ssam cal.monthday = (u_char)((cal.monthday << 3) + (cal.monthday << 1)); 2001178354Ssam cal.monthday = (u_char)(cal.monthday + *cp++ - '0'); 2002178354Ssam } 2003178354Ssam 2004178354Ssam if (*cp++ != '-') 2005178354Ssam return 0; 2006178354Ssam 2007178354Ssam for (i = 0; i < 3; i++) 2008178354Ssam buf[i] = *cp++; 2009178354Ssam buf[3] = '\0'; 2010190391Ssam 2011178354Ssam for (i = 0; i < 12; i++) 2012178354Ssam if (STREQ(buf, months[i])) 2013190391Ssam break; 2014193655Ssam if (i == 12) 2015178354Ssam return 0; 2016178354Ssam cal.month = (u_char)(i + 1); 2017178354Ssam 2018178354Ssam if (*cp++ != '-') 2019178354Ssam return 0; 2020178354Ssam 2021178354Ssam if (!isdigit((int)*cp)) 2022178354Ssam return 0; 2023178354Ssam cal.year = (u_short)(*cp++ - '0'); 2024178354Ssam if (isdigit((int)*cp)) { 2025178354Ssam cal.year = (u_short)((cal.year << 3) + (cal.year << 1)); 2026178354Ssam cal.year = (u_short)(*cp++ - '0'); 2027178354Ssam } 2028178354Ssam if (isdigit((int)*cp)) { 2029178354Ssam cal.year = (u_short)((cal.year << 3) + (cal.year << 1)); 2030178354Ssam cal.year = (u_short)(cal.year + *cp++ - '0'); 2031178354Ssam } 2032178354Ssam if (isdigit((int)*cp)) { 2033178354Ssam cal.year = (u_short)((cal.year << 3) + (cal.year << 1)); 2034178354Ssam cal.year = (u_short)(cal.year + *cp++ - '0'); 2035178354Ssam } 2036178354Ssam 2037178354Ssam /* 2038178354Ssam * Catch special case. If cal.year == 0 this is a zero timestamp. 2039178354Ssam */ 2040178354Ssam if (cal.year == 0) { 2041186302Ssam L_CLR(lfp); 2042178354Ssam return 1; 2043178354Ssam } 2044178354Ssam 2045178354Ssam if (*cp++ != ' ' || !isdigit((int)*cp)) 2046178354Ssam return 0; 2047178354Ssam cal.hour = (u_char)(*cp++ - '0'); 2048178354Ssam if (isdigit((int)*cp)) { 2049178354Ssam cal.hour = (u_char)((cal.hour << 3) + (cal.hour << 1)); 2050178354Ssam cal.hour = (u_char)(cal.hour + *cp++ - '0'); 2051178354Ssam } 2052178354Ssam 2053178354Ssam if (*cp++ != ':' || !isdigit((int)*cp)) 2054178354Ssam return 0; 2055178354Ssam cal.minute = (u_char)(*cp++ - '0'); 2056178354Ssam if (isdigit((int)*cp)) { 2057178354Ssam cal.minute = (u_char)((cal.minute << 3) + (cal.minute << 1)); 2058178354Ssam cal.minute = (u_char)(cal.minute + *cp++ - '0'); 2059178354Ssam } 2060178354Ssam 2061178354Ssam if (*cp++ != ':' || !isdigit((int)*cp)) 2062178354Ssam return 0; 2063178354Ssam cal.second = (u_char)(*cp++ - '0'); 2064178354Ssam if (isdigit((int)*cp)) { 2065178354Ssam cal.second = (u_char)((cal.second << 3) + (cal.second << 1)); 2066178354Ssam cal.second = (u_char)(cal.second + *cp++ - '0'); 2067178354Ssam } 2068178354Ssam 2069178354Ssam /* 2070178354Ssam * For RT-11, 1972 seems to be the pivot year 2071178354Ssam */ 2072178354Ssam if (cal.year < 72) 2073178354Ssam cal.year += 2000; 2074178354Ssam if (cal.year < 100) 2075178354Ssam cal.year += 1900; 2076178354Ssam 2077178354Ssam lfp->l_ui = caltontp(&cal); 2078178354Ssam lfp->l_uf = 0; 2079178354Ssam return 1; 2080178354Ssam} 2081178354Ssam 2082178354Ssam 2083178354Ssam/* 2084178354Ssam * decodets - decode a timestamp into an l_fp format number, with 2085178354Ssam * consideration of fuzzball formats. 2086178354Ssam */ 2087178354Ssamint 2088178354Ssamdecodets( 2089178354Ssam char *str, 2090178354Ssam l_fp *lfp 2091178354Ssam ) 2092178354Ssam{ 2093183254Ssam char *cp; 2094183254Ssam char buf[30]; 2095178354Ssam size_t b; 2096178354Ssam 2097190579Ssam /* 2098190579Ssam * If it starts with a 0x, decode as hex. 2099190579Ssam */ 2100190579Ssam if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X')) 2101178354Ssam return hextolfp(str+2, lfp); 2102178354Ssam 2103178354Ssam /* 2104178354Ssam * If it starts with a '"', try it as an RT-11 date. 2105178354Ssam */ 2106178354Ssam if (*str == '"') { 2107178354Ssam cp = str + 1; 2108178354Ssam b = 0; 2109178354Ssam while ('"' != *cp && '\0' != *cp && 2110178354Ssam b < COUNTOF(buf) - 1) 2111178354Ssam buf[b++] = *cp++; 2112178354Ssam buf[b] = '\0'; 2113178354Ssam return rtdatetolfp(buf, lfp); 2114178354Ssam } 2115178354Ssam 2116178354Ssam /* 2117178354Ssam * Might still be hex. Check out the first character. Talk 2118178354Ssam * about heuristics! 2119178354Ssam */ 2120178354Ssam if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f')) 2121178354Ssam return hextolfp(str, lfp); 2122193655Ssam 2123178354Ssam /* 2124178354Ssam * Try it as a decimal. If this fails, try as an unquoted 2125178354Ssam * RT-11 date. This code should go away eventually. 2126178354Ssam */ 2127178354Ssam if (atolfp(str, lfp)) 2128178354Ssam return 1; 2129192468Ssam 2130178354Ssam return rtdatetolfp(str, lfp); 2131178354Ssam} 2132178354Ssam 2133178354Ssam 2134178354Ssam/* 2135178354Ssam * decodetime - decode a time value. It should be in milliseconds 2136178354Ssam */ 2137178354Ssamint 2138178354Ssamdecodetime( 2139178354Ssam char *str, 2140178354Ssam l_fp *lfp 2141178354Ssam ) 2142178354Ssam{ 2143178354Ssam return mstolfp(str, lfp); 2144178354Ssam} 2145178354Ssam 2146178354Ssam 2147178354Ssam/* 2148178354Ssam * decodeint - decode an integer 2149178354Ssam */ 2150178354Ssamint 2151178354Ssamdecodeint( 2152178354Ssam char *str, 2153178354Ssam long *val 2154190391Ssam ) 2155178354Ssam{ 2156178354Ssam if (*str == '0') { 2157178354Ssam if (*(str+1) == 'x' || *(str+1) == 'X') 2158178354Ssam return hextoint(str+2, (u_long *)val); 2159178354Ssam return octtoint(str, (u_long *)val); 2160178354Ssam } 2161178354Ssam return atoint(str, val); 2162190391Ssam} 2163178354Ssam 2164178354Ssam 2165178354Ssam/* 2166178354Ssam * decodeuint - decode an unsigned integer 2167178354Ssam */ 2168178354Ssamint 2169178354Ssamdecodeuint( 2170178354Ssam char *str, 2171178354Ssam u_long *val 2172178354Ssam ) 2173178354Ssam{ 2174178354Ssam if (*str == '0') { 2175178354Ssam if (*(str + 1) == 'x' || *(str + 1) == 'X') 2176178354Ssam return (hextoint(str + 2, val)); 2177178354Ssam return (octtoint(str, val)); 2178178354Ssam } 2179178354Ssam return (atouint(str, val)); 2180178354Ssam} 2181178354Ssam 2182178354Ssam 2183178354Ssam/* 2184178354Ssam * decodearr - decode an array of time values 2185178354Ssam */ 2186178354Ssamstatic int 2187178354Ssamdecodearr( 2188178354Ssam char *str, 2189178354Ssam int *narr, 2190178354Ssam l_fp *lfparr 2191178354Ssam ) 2192178354Ssam{ 2193178354Ssam register char *cp, *bp; 2194178354Ssam register l_fp *lfp; 2195178354Ssam char buf[60]; 2196178354Ssam 2197178354Ssam lfp = lfparr; 2198178354Ssam cp = str; 2199178354Ssam *narr = 0; 2200178354Ssam 2201178354Ssam while (*narr < 8) { 2202178354Ssam while (isspace((int)*cp)) 2203178354Ssam cp++; 2204178354Ssam if (*cp == '\0') 2205178354Ssam break; 2206218927Sbschmidt 2207218958Sbschmidt bp = buf; 2208218927Sbschmidt while (!isspace((int)*cp) && *cp != '\0') 2209218958Sbschmidt *bp++ = *cp++; 2210218958Sbschmidt *bp++ = '\0'; 2211218958Sbschmidt 2212218958Sbschmidt if (!decodetime(buf, lfp)) 2213218958Sbschmidt return 0; 2214218958Sbschmidt (*narr)++; 2215218958Sbschmidt lfp++; 2216218958Sbschmidt } 2217218958Sbschmidt return 1; 2218218927Sbschmidt} 2219218927Sbschmidt 2220178354Ssam 2221218958Sbschmidt/* 2222218958Sbschmidt * Finally, the built in command handlers 2223218958Sbschmidt */ 2224218927Sbschmidt 2225178354Ssam/* 2226178354Ssam * help - tell about commands, or details of a particular command 2227178354Ssam */ 2228178354Ssamstatic void 2229218927Sbschmidthelp( 2230218927Sbschmidt struct parse *pcmd, 2231218927Sbschmidt FILE *fp 2232218927Sbschmidt ) 2233218927Sbschmidt{ 2234218927Sbschmidt struct xcmd *xcp = NULL; /* quiet warning */ 2235178354Ssam const char *cmd; 2236178354Ssam const char *list[100]; 2237218927Sbschmidt size_t word, words; 2238178354Ssam size_t row, rows; 2239178354Ssam size_t col, cols; 2240178354Ssam size_t length; 2241178354Ssam 2242178354Ssam if (pcmd->nargs == 0) { 2243191546Ssam words = 0; 2244191546Ssam for (xcp = builtins; xcp->keyword != NULL; xcp++) { 2245191546Ssam if (*(xcp->keyword) != '?' && 2246191546Ssam words < COUNTOF(list)) 2247191546Ssam list[words++] = xcp->keyword; 2248241138Sadrian } 2249191546Ssam for (xcp = opcmds; xcp->keyword != NULL; xcp++) 2250191546Ssam if (words < COUNTOF(list)) 2251191546Ssam list[words++] = xcp->keyword; 2252191546Ssam 2253191546Ssam qsort((void *)list, words, sizeof(list[0]), helpsort); 2254191546Ssam col = 0; 2255191546Ssam for (word = 0; word < words; word++) { 2256178354Ssam length = strlen(list[word]); 2257178354Ssam col = max(col, length); 2258178354Ssam } 2259241138Sadrian 2260241138Sadrian cols = SCREENWIDTH / ++col; 2261178354Ssam rows = (words + cols - 1) / cols; 2262178354Ssam 2263248069Sadrian fprintf(fp, "ntpq commands:\n"); 2264178354Ssam 2265178354Ssam for (row = 0; row < rows; row++) { 2266178354Ssam for (word = row; word < words; word += rows) 2267178354Ssam fprintf(fp, "%-*.*s", (int)col, 2268178354Ssam (int)col - 1, list[word]); 2269178354Ssam fprintf(fp, "\n"); 2270178354Ssam } 2271178354Ssam } else { 2272178354Ssam cmd = pcmd->argval[0].string; 2273178354Ssam words = findcmd(cmd, builtins, opcmds, &xcp); 2274178354Ssam if (words == 0) { 2275178354Ssam fprintf(stderr, 2276178354Ssam "Command `%s' is unknown\n", cmd); 2277178354Ssam return; 2278178354Ssam } else if (words >= 2) { 2279178354Ssam fprintf(stderr, 2280178354Ssam "Command `%s' is ambiguous\n", cmd); 2281178354Ssam return; 2282178354Ssam } 2283178354Ssam fprintf(fp, "function: %s\n", xcp->comment); 2284178354Ssam printusage(xcp, fp); 2285178354Ssam } 2286178354Ssam} 2287178354Ssam 2288178354Ssam 2289180837Ssam/* 2290180837Ssam * helpsort - do hostname qsort comparisons 2291180837Ssam */ 2292180837Ssamstatic int 2293180837Ssamhelpsort( 2294180837Ssam const void *t1, 2295180837Ssam const void *t2 2296180837Ssam ) 2297178354Ssam{ 2298178354Ssam const char * const * name1 = t1; 2299178354Ssam const char * const * name2 = t2; 2300178354Ssam 2301184288Ssam return strcmp(*name1, *name2); 2302178354Ssam} 2303178354Ssam 2304178354Ssam 2305178354Ssam/* 2306178354Ssam * printusage - print usage information for a command 2307178354Ssam */ 2308178354Ssamstatic void 2309178354Ssamprintusage( 2310178354Ssam struct xcmd *xcp, 2311178354Ssam FILE *fp 2312178354Ssam ) 2313178354Ssam{ 2314178354Ssam register int i; 2315178354Ssam 2316178354Ssam /* XXX: Do we need to warn about extra args here too? */ 2317178354Ssam 2318178354Ssam (void) fprintf(fp, "usage: %s", xcp->keyword); 2319178354Ssam for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) { 2320178354Ssam if (xcp->arg[i] & OPT) 2321178354Ssam (void) fprintf(fp, " [ %s ]", xcp->desc[i]); 2322178354Ssam else 2323178354Ssam (void) fprintf(fp, " %s", xcp->desc[i]); 2324178354Ssam } 2325178354Ssam (void) fprintf(fp, "\n"); 2326178354Ssam} 2327184288Ssam 2328245098Sadrian 2329248069Sadrian/* 2330254082Sadrian * timeout - set time out time 2331254082Sadrian */ 2332245098Sadrianstatic void 2333248069Sadriantimeout( 2334254082Sadrian struct parse *pcmd, 2335245098Sadrian FILE *fp 2336248069Sadrian ) 2337254082Sadrian{ 2338245098Sadrian int val; 2339178354Ssam 2340 if (pcmd->nargs == 0) { 2341 val = (int)tvout.tv_sec * 1000 + tvout.tv_usec / 1000; 2342 (void) fprintf(fp, "primary timeout %d ms\n", val); 2343 } else { 2344 tvout.tv_sec = pcmd->argval[0].uval / 1000; 2345 tvout.tv_usec = (pcmd->argval[0].uval - ((long)tvout.tv_sec * 1000)) 2346 * 1000; 2347 } 2348} 2349 2350 2351/* 2352 * auth_delay - set delay for auth requests 2353 */ 2354static void 2355auth_delay( 2356 struct parse *pcmd, 2357 FILE *fp 2358 ) 2359{ 2360 int isneg; 2361 u_long val; 2362 2363 if (pcmd->nargs == 0) { 2364 val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967; 2365 (void) fprintf(fp, "delay %lu ms\n", val); 2366 } else { 2367 if (pcmd->argval[0].ival < 0) { 2368 isneg = 1; 2369 val = (u_long)(-pcmd->argval[0].ival); 2370 } else { 2371 isneg = 0; 2372 val = (u_long)pcmd->argval[0].ival; 2373 } 2374 2375 delay_time.l_ui = val / 1000; 2376 val %= 1000; 2377 delay_time.l_uf = val * 4294967; /* 2**32/1000 */ 2378 2379 if (isneg) 2380 L_NEG(&delay_time); 2381 } 2382} 2383 2384 2385/* 2386 * host - set the host we are dealing with. 2387 */ 2388static void 2389host( 2390 struct parse *pcmd, 2391 FILE *fp 2392 ) 2393{ 2394 int i; 2395 2396 if (pcmd->nargs == 0) { 2397 if (havehost) 2398 (void) fprintf(fp, "current host is %s\n", 2399 currenthost); 2400 else 2401 (void) fprintf(fp, "no current host\n"); 2402 return; 2403 } 2404 2405 i = 0; 2406 ai_fam_templ = ai_fam_default; 2407 if (pcmd->nargs == 2) { 2408 if (!strcmp("-4", pcmd->argval[i].string)) 2409 ai_fam_templ = AF_INET; 2410 else if (!strcmp("-6", pcmd->argval[i].string)) 2411 ai_fam_templ = AF_INET6; 2412 else 2413 goto no_change; 2414 i = 1; 2415 } 2416 if (openhost(pcmd->argval[i].string, ai_fam_templ)) { 2417 fprintf(fp, "current host set to %s\n", currenthost); 2418 } else { 2419 no_change: 2420 if (havehost) 2421 fprintf(fp, "current host remains %s\n", 2422 currenthost); 2423 else 2424 fprintf(fp, "still no current host\n"); 2425 } 2426} 2427 2428 2429/* 2430 * poll - do one (or more) polls of the host via NTP 2431 */ 2432/*ARGSUSED*/ 2433static void 2434ntp_poll( 2435 struct parse *pcmd, 2436 FILE *fp 2437 ) 2438{ 2439 (void) fprintf(fp, "poll not implemented yet\n"); 2440} 2441 2442 2443/* 2444 * keyid - get a keyid to use for authenticating requests 2445 */ 2446static void 2447keyid( 2448 struct parse *pcmd, 2449 FILE *fp 2450 ) 2451{ 2452 if (pcmd->nargs == 0) { 2453 if (info_auth_keyid == 0) 2454 (void) fprintf(fp, "no keyid defined\n"); 2455 else 2456 (void) fprintf(fp, "keyid is %lu\n", (u_long)info_auth_keyid); 2457 } else { 2458 /* allow zero so that keyid can be cleared. */ 2459 if(pcmd->argval[0].uval > NTP_MAXKEY) 2460 (void) fprintf(fp, "Invalid key identifier\n"); 2461 info_auth_keyid = pcmd->argval[0].uval; 2462 } 2463} 2464 2465/* 2466 * keytype - get type of key to use for authenticating requests 2467 */ 2468static void 2469keytype( 2470 struct parse *pcmd, 2471 FILE *fp 2472 ) 2473{ 2474 const char * digest_name; 2475 size_t digest_len; 2476 int key_type; 2477 2478 if (!pcmd->nargs) { 2479 fprintf(fp, "keytype is %s with %lu octet digests\n", 2480 keytype_name(info_auth_keytype), 2481 (u_long)info_auth_hashlen); 2482 return; 2483 } 2484 2485 digest_name = pcmd->argval[0].string; 2486 digest_len = 0; 2487 key_type = keytype_from_text(digest_name, &digest_len); 2488 2489 if (!key_type) { 2490 fprintf(fp, "keytype is not valid. " 2491#ifdef OPENSSL 2492 "Type \"help keytype\" for the available digest types.\n"); 2493#else 2494 "Only \"md5\" is available.\n"); 2495#endif 2496 return; 2497 } 2498 2499 info_auth_keytype = key_type; 2500 info_auth_hashlen = digest_len; 2501} 2502 2503 2504/* 2505 * passwd - get an authentication key 2506 */ 2507/*ARGSUSED*/ 2508static void 2509passwd( 2510 struct parse *pcmd, 2511 FILE *fp 2512 ) 2513{ 2514 const char *pass; 2515 2516 if (info_auth_keyid == 0) { 2517 info_auth_keyid = getkeyid("Keyid: "); 2518 if (info_auth_keyid == 0) { 2519 (void)fprintf(fp, "Keyid must be defined\n"); 2520 return; 2521 } 2522 } 2523 if (pcmd->nargs >= 1) 2524 pass = pcmd->argval[0].string; 2525 else { 2526 pass = getpass_keytype(info_auth_keytype); 2527 if ('\0' == pass[0]) { 2528 fprintf(fp, "Password unchanged\n"); 2529 return; 2530 } 2531 } 2532 authusekey(info_auth_keyid, info_auth_keytype, 2533 (const u_char *)pass); 2534 authtrust(info_auth_keyid, 1); 2535} 2536 2537 2538/* 2539 * hostnames - set the showhostnames flag 2540 */ 2541static void 2542hostnames( 2543 struct parse *pcmd, 2544 FILE *fp 2545 ) 2546{ 2547 if (pcmd->nargs == 0) { 2548 if (showhostnames) 2549 (void) fprintf(fp, "hostnames being shown\n"); 2550 else 2551 (void) fprintf(fp, "hostnames not being shown\n"); 2552 } else { 2553 if (STREQ(pcmd->argval[0].string, "yes")) 2554 showhostnames = 1; 2555 else if (STREQ(pcmd->argval[0].string, "no")) 2556 showhostnames = 0; 2557 else 2558 (void)fprintf(stderr, "What?\n"); 2559 } 2560} 2561 2562 2563 2564/* 2565 * setdebug - set/change debugging level 2566 */ 2567static void 2568setdebug( 2569 struct parse *pcmd, 2570 FILE *fp 2571 ) 2572{ 2573 if (pcmd->nargs == 0) { 2574 (void) fprintf(fp, "debug level is %d\n", debug); 2575 return; 2576 } else if (STREQ(pcmd->argval[0].string, "no")) { 2577 debug = 0; 2578 } else if (STREQ(pcmd->argval[0].string, "more")) { 2579 debug++; 2580 } else if (STREQ(pcmd->argval[0].string, "less")) { 2581 debug--; 2582 } else { 2583 (void) fprintf(fp, "What?\n"); 2584 return; 2585 } 2586 (void) fprintf(fp, "debug level set to %d\n", debug); 2587} 2588 2589 2590/* 2591 * quit - stop this nonsense 2592 */ 2593/*ARGSUSED*/ 2594static void 2595quit( 2596 struct parse *pcmd, 2597 FILE *fp 2598 ) 2599{ 2600 if (havehost) 2601 closesocket(sockfd); /* cleanliness next to godliness */ 2602 exit(0); 2603} 2604 2605 2606/* 2607 * version - print the current version number 2608 */ 2609/*ARGSUSED*/ 2610static void 2611version( 2612 struct parse *pcmd, 2613 FILE *fp 2614 ) 2615{ 2616 2617 (void) fprintf(fp, "%s\n", Version); 2618 return; 2619} 2620 2621 2622/* 2623 * raw - set raw mode output 2624 */ 2625/*ARGSUSED*/ 2626static void 2627raw( 2628 struct parse *pcmd, 2629 FILE *fp 2630 ) 2631{ 2632 rawmode = 1; 2633 (void) fprintf(fp, "Output set to raw\n"); 2634} 2635 2636 2637/* 2638 * cooked - set cooked mode output 2639 */ 2640/*ARGSUSED*/ 2641static void 2642cooked( 2643 struct parse *pcmd, 2644 FILE *fp 2645 ) 2646{ 2647 rawmode = 0; 2648 (void) fprintf(fp, "Output set to cooked\n"); 2649 return; 2650} 2651 2652 2653/* 2654 * authenticate - always authenticate requests to this host 2655 */ 2656static void 2657authenticate( 2658 struct parse *pcmd, 2659 FILE *fp 2660 ) 2661{ 2662 if (pcmd->nargs == 0) { 2663 if (always_auth) { 2664 (void) fprintf(fp, 2665 "authenticated requests being sent\n"); 2666 } else 2667 (void) fprintf(fp, 2668 "unauthenticated requests being sent\n"); 2669 } else { 2670 if (STREQ(pcmd->argval[0].string, "yes")) { 2671 always_auth = 1; 2672 } else if (STREQ(pcmd->argval[0].string, "no")) { 2673 always_auth = 0; 2674 } else 2675 (void)fprintf(stderr, "What?\n"); 2676 } 2677} 2678 2679 2680/* 2681 * ntpversion - choose the NTP version to use 2682 */ 2683static void 2684ntpversion( 2685 struct parse *pcmd, 2686 FILE *fp 2687 ) 2688{ 2689 if (pcmd->nargs == 0) { 2690 (void) fprintf(fp, 2691 "NTP version being claimed is %d\n", pktversion); 2692 } else { 2693 if (pcmd->argval[0].uval < NTP_OLDVERSION 2694 || pcmd->argval[0].uval > NTP_VERSION) { 2695 (void) fprintf(stderr, "versions %d to %d, please\n", 2696 NTP_OLDVERSION, NTP_VERSION); 2697 } else { 2698 pktversion = (u_char) pcmd->argval[0].uval; 2699 } 2700 } 2701} 2702 2703 2704static void __attribute__((__format__(__printf__, 1, 0))) 2705vwarning(const char *fmt, va_list ap) 2706{ 2707 int serrno = errno; 2708 (void) fprintf(stderr, "%s: ", progname); 2709 vfprintf(stderr, fmt, ap); 2710 (void) fprintf(stderr, ": %s\n", strerror(serrno)); 2711} 2712 2713/* 2714 * warning - print a warning message 2715 */ 2716static void __attribute__((__format__(__printf__, 1, 2))) 2717warning( 2718 const char *fmt, 2719 ... 2720 ) 2721{ 2722 va_list ap; 2723 va_start(ap, fmt); 2724 vwarning(fmt, ap); 2725 va_end(ap); 2726} 2727 2728 2729/* 2730 * error - print a message and exit 2731 */ 2732static void __attribute__((__format__(__printf__, 1, 2))) 2733error( 2734 const char *fmt, 2735 ... 2736 ) 2737{ 2738 va_list ap; 2739 va_start(ap, fmt); 2740 vwarning(fmt, ap); 2741 va_end(ap); 2742 exit(1); 2743} 2744/* 2745 * getkeyid - prompt the user for a keyid to use 2746 */ 2747static u_long 2748getkeyid( 2749 const char *keyprompt 2750 ) 2751{ 2752 int c; 2753 FILE *fi; 2754 char pbuf[20]; 2755 size_t i; 2756 size_t ilim; 2757 2758#ifndef SYS_WINNT 2759 if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL) 2760#else 2761 if ((fi = _fdopen(open("CONIN$", _O_TEXT), "r")) == NULL) 2762#endif /* SYS_WINNT */ 2763 fi = stdin; 2764 else 2765 setbuf(fi, (char *)NULL); 2766 fprintf(stderr, "%s", keyprompt); fflush(stderr); 2767 for (i = 0, ilim = COUNTOF(pbuf) - 1; 2768 i < ilim && (c = getc(fi)) != '\n' && c != EOF; 2769 ) 2770 pbuf[i++] = (char)c; 2771 pbuf[i] = '\0'; 2772 if (fi != stdin) 2773 fclose(fi); 2774 2775 return (u_long) atoi(pbuf); 2776} 2777 2778 2779/* 2780 * atoascii - printable-ize possibly ascii data using the character 2781 * transformations cat -v uses. 2782 */ 2783static void 2784atoascii( 2785 const char *in, 2786 size_t in_octets, 2787 char *out, 2788 size_t out_octets 2789 ) 2790{ 2791 const u_char * pchIn; 2792 const u_char * pchInLimit; 2793 u_char * pchOut; 2794 u_char c; 2795 2796 pchIn = (const u_char *)in; 2797 pchInLimit = pchIn + in_octets; 2798 pchOut = (u_char *)out; 2799 2800 if (NULL == pchIn) { 2801 if (0 < out_octets) 2802 *pchOut = '\0'; 2803 return; 2804 } 2805 2806#define ONEOUT(c) \ 2807do { \ 2808 if (0 == --out_octets) { \ 2809 *pchOut = '\0'; \ 2810 return; \ 2811 } \ 2812 *pchOut++ = (c); \ 2813} while (0) 2814 2815 for ( ; pchIn < pchInLimit; pchIn++) { 2816 c = *pchIn; 2817 if ('\0' == c) 2818 break; 2819 if (c & 0x80) { 2820 ONEOUT('M'); 2821 ONEOUT('-'); 2822 c &= 0x7f; 2823 } 2824 if (c < ' ') { 2825 ONEOUT('^'); 2826 ONEOUT((u_char)(c + '@')); 2827 } else if (0x7f == c) { 2828 ONEOUT('^'); 2829 ONEOUT('?'); 2830 } else 2831 ONEOUT(c); 2832 } 2833 ONEOUT('\0'); 2834 2835#undef ONEOUT 2836} 2837 2838 2839/* 2840 * makeascii - print possibly ascii data using the character 2841 * transformations that cat -v uses. 2842 */ 2843void 2844makeascii( 2845 size_t length, 2846 const char *data, 2847 FILE *fp 2848 ) 2849{ 2850 const u_char *data_u_char; 2851 const u_char *cp; 2852 int c; 2853 2854 data_u_char = (const u_char *)data; 2855 2856 for (cp = data_u_char; cp < data_u_char + length; cp++) { 2857 c = (int)*cp; 2858 if (c & 0x80) { 2859 putc('M', fp); 2860 putc('-', fp); 2861 c &= 0x7f; 2862 } 2863 2864 if (c < ' ') { 2865 putc('^', fp); 2866 putc(c + '@', fp); 2867 } else if (0x7f == c) { 2868 putc('^', fp); 2869 putc('?', fp); 2870 } else 2871 putc(c, fp); 2872 } 2873} 2874 2875 2876/* 2877 * asciize - same thing as makeascii except add a newline 2878 */ 2879void 2880asciize( 2881 int length, 2882 char *data, 2883 FILE *fp 2884 ) 2885{ 2886 makeascii(length, data, fp); 2887 putc('\n', fp); 2888} 2889 2890 2891/* 2892 * truncate string to fit clipping excess at end. 2893 * "too long" -> "too l" 2894 * Used for hostnames. 2895 */ 2896const char * 2897trunc_right( 2898 const char * src, 2899 size_t width 2900 ) 2901{ 2902 size_t sl; 2903 char * out; 2904 2905 2906 sl = strlen(src); 2907 if (sl > width && LIB_BUFLENGTH - 1 > width && width > 0) { 2908 LIB_GETBUF(out); 2909 memcpy(out, src, width); 2910 out[width] = '\0'; 2911 2912 return out; 2913 } 2914 2915 return src; 2916} 2917 2918 2919/* 2920 * truncate string to fit by preserving right side and using '_' to hint 2921 * "too long" -> "_long" 2922 * Used for local IPv6 addresses, where low bits differentiate. 2923 */ 2924const char * 2925trunc_left( 2926 const char * src, 2927 size_t width 2928 ) 2929{ 2930 size_t sl; 2931 char * out; 2932 2933 2934 sl = strlen(src); 2935 if (sl > width && LIB_BUFLENGTH - 1 > width && width > 1) { 2936 LIB_GETBUF(out); 2937 out[0] = '_'; 2938 memcpy(&out[1], &src[sl + 1 - width], width); 2939 2940 return out; 2941 } 2942 2943 return src; 2944} 2945 2946 2947/* 2948 * Some circular buffer space 2949 */ 2950#define CBLEN 80 2951#define NUMCB 6 2952 2953char circ_buf[NUMCB][CBLEN]; 2954int nextcb = 0; 2955 2956/* 2957 * nextvar - find the next variable in the buffer 2958 */ 2959int 2960nextvar( 2961 size_t *datalen, 2962 const char **datap, 2963 char **vname, 2964 char **vvalue 2965 ) 2966{ 2967 const char *cp; 2968 const char *np; 2969 const char *cpend; 2970 size_t srclen; 2971 size_t len; 2972 static char name[MAXVARLEN]; 2973 static char value[MAXVALLEN]; 2974 2975 cp = *datap; 2976 cpend = cp + *datalen; 2977 2978 /* 2979 * Space past commas and white space 2980 */ 2981 while (cp < cpend && (*cp == ',' || isspace((int)*cp))) 2982 cp++; 2983 if (cp >= cpend) 2984 return 0; 2985 2986 /* 2987 * Copy name until we hit a ',', an '=', a '\r' or a '\n'. Backspace 2988 * over any white space and terminate it. 2989 */ 2990 srclen = strcspn(cp, ",=\r\n"); 2991 srclen = min(srclen, (size_t)(cpend - cp)); 2992 len = srclen; 2993 while (len > 0 && isspace((unsigned char)cp[len - 1])) 2994 len--; 2995 if (len >= sizeof(name)) 2996 return 0; 2997 if (len > 0) 2998 memcpy(name, cp, len); 2999 name[len] = '\0'; 3000 *vname = name; 3001 cp += srclen; 3002 3003 /* 3004 * Check if we hit the end of the buffer or a ','. If so we are done. 3005 */ 3006 if (cp >= cpend || *cp == ',' || *cp == '\r' || *cp == '\n') { 3007 if (cp < cpend) 3008 cp++; 3009 *datap = cp; 3010 *datalen = size2int_sat(cpend - cp); 3011 *vvalue = NULL; 3012 return 1; 3013 } 3014 3015 /* 3016 * So far, so good. Copy out the value 3017 */ 3018 cp++; /* past '=' */ 3019 while (cp < cpend && (isspace((unsigned char)*cp) && *cp != '\r' && *cp != '\n')) 3020 cp++; 3021 np = cp; 3022 if ('"' == *np) { 3023 do { 3024 np++; 3025 } while (np < cpend && '"' != *np); 3026 if (np < cpend && '"' == *np) 3027 np++; 3028 } else { 3029 while (np < cpend && ',' != *np && '\r' != *np) 3030 np++; 3031 } 3032 len = np - cp; 3033 if (np > cpend || len >= sizeof(value) || 3034 (np < cpend && ',' != *np && '\r' != *np)) 3035 return 0; 3036 memcpy(value, cp, len); 3037 /* 3038 * Trim off any trailing whitespace 3039 */ 3040 while (len > 0 && isspace((unsigned char)value[len - 1])) 3041 len--; 3042 value[len] = '\0'; 3043 3044 /* 3045 * Return this. All done. 3046 */ 3047 if (np < cpend && ',' == *np) 3048 np++; 3049 *datap = np; 3050 *datalen = size2int_sat(cpend - np); 3051 *vvalue = value; 3052 return 1; 3053} 3054 3055 3056u_short 3057varfmt(const char * varname) 3058{ 3059 u_int n; 3060 3061 for (n = 0; n < COUNTOF(cookedvars); n++) 3062 if (!strcmp(varname, cookedvars[n].varname)) 3063 return cookedvars[n].fmt; 3064 3065 return PADDING; 3066} 3067 3068 3069/* 3070 * printvars - print variables returned in response packet 3071 */ 3072void 3073printvars( 3074 size_t length, 3075 const char *data, 3076 int status, 3077 int sttype, 3078 int quiet, 3079 FILE *fp 3080 ) 3081{ 3082 if (rawmode) 3083 rawprint(sttype, length, data, status, quiet, fp); 3084 else 3085 cookedprint(sttype, length, data, status, quiet, fp); 3086} 3087 3088 3089/* 3090 * rawprint - do a printout of the data in raw mode 3091 */ 3092static void 3093rawprint( 3094 int datatype, 3095 size_t length, 3096 const char *data, 3097 int status, 3098 int quiet, 3099 FILE *fp 3100 ) 3101{ 3102 const char *cp; 3103 const char *cpend; 3104 3105 /* 3106 * Essentially print the data as is. We reformat unprintables, though. 3107 */ 3108 cp = data; 3109 cpend = data + length; 3110 3111 if (!quiet) 3112 (void) fprintf(fp, "status=0x%04x,\n", status); 3113 3114 while (cp < cpend) { 3115 if (*cp == '\r') { 3116 /* 3117 * If this is a \r and the next character is a 3118 * \n, supress this, else pretty print it. Otherwise 3119 * just output the character. 3120 */ 3121 if (cp == (cpend - 1) || *(cp + 1) != '\n') 3122 makeascii(1, cp, fp); 3123 } else if (isspace((unsigned char)*cp) || isprint((unsigned char)*cp)) 3124 putc(*cp, fp); 3125 else 3126 makeascii(1, cp, fp); 3127 cp++; 3128 } 3129} 3130 3131 3132/* 3133 * Global data used by the cooked output routines 3134 */ 3135int out_chars; /* number of characters output */ 3136int out_linecount; /* number of characters output on this line */ 3137 3138 3139/* 3140 * startoutput - get ready to do cooked output 3141 */ 3142static void 3143startoutput(void) 3144{ 3145 out_chars = 0; 3146 out_linecount = 0; 3147} 3148 3149 3150/* 3151 * output - output a variable=value combination 3152 */ 3153static void 3154output( 3155 FILE *fp, 3156 const char *name, 3157 const char *value 3158 ) 3159{ 3160 int len; 3161 3162 /* strlen of "name=value" */ 3163 len = size2int_sat(strlen(name) + 1 + strlen(value)); 3164 3165 if (out_chars != 0) { 3166 out_chars += 2; 3167 if ((out_linecount + len + 2) > MAXOUTLINE) { 3168 fputs(",\n", fp); 3169 out_linecount = 0; 3170 } else { 3171 fputs(", ", fp); 3172 out_linecount += 2; 3173 } 3174 } 3175 3176 fputs(name, fp); 3177 putc('=', fp); 3178 fputs(value, fp); 3179 out_chars += len; 3180 out_linecount += len; 3181} 3182 3183 3184/* 3185 * endoutput - terminate a block of cooked output 3186 */ 3187static void 3188endoutput( 3189 FILE *fp 3190 ) 3191{ 3192 if (out_chars != 0) 3193 putc('\n', fp); 3194} 3195 3196 3197/* 3198 * outputarr - output an array of values 3199 */ 3200static void 3201outputarr( 3202 FILE *fp, 3203 char *name, 3204 int narr, 3205 l_fp *lfp 3206 ) 3207{ 3208 char *bp; 3209 char *cp; 3210 size_t i; 3211 size_t len; 3212 char buf[256]; 3213 3214 bp = buf; 3215 /* 3216 * Hack to align delay and offset values 3217 */ 3218 for (i = (int)strlen(name); i < 11; i++) 3219 *bp++ = ' '; 3220 3221 for (i = narr; i > 0; i--) { 3222 if (i != narr) 3223 *bp++ = ' '; 3224 cp = lfptoms(lfp, 2); 3225 len = strlen(cp); 3226 if (len > 7) { 3227 cp[7] = '\0'; 3228 len = 7; 3229 } 3230 while (len < 7) { 3231 *bp++ = ' '; 3232 len++; 3233 } 3234 while (*cp != '\0') 3235 *bp++ = *cp++; 3236 lfp++; 3237 } 3238 *bp = '\0'; 3239 output(fp, name, buf); 3240} 3241 3242static char * 3243tstflags( 3244 u_long val 3245 ) 3246{ 3247 register char *cp, *s; 3248 size_t cb; 3249 register int i; 3250 register const char *sep; 3251 3252 sep = ""; 3253 s = cp = circ_buf[nextcb]; 3254 if (++nextcb >= NUMCB) 3255 nextcb = 0; 3256 cb = sizeof(circ_buf[0]); 3257 3258 snprintf(cp, cb, "%02lx", val); 3259 cp += strlen(cp); 3260 cb -= strlen(cp); 3261 if (!val) { 3262 strlcat(cp, " ok", cb); 3263 cp += strlen(cp); 3264 cb -= strlen(cp); 3265 } else { 3266 if (cb) { 3267 *cp++ = ' '; 3268 cb--; 3269 } 3270 for (i = 0; i < (int)COUNTOF(tstflagnames); i++) { 3271 if (val & 0x1) { 3272 snprintf(cp, cb, "%s%s", sep, 3273 tstflagnames[i]); 3274 sep = ", "; 3275 cp += strlen(cp); 3276 cb -= strlen(cp); 3277 } 3278 val >>= 1; 3279 } 3280 } 3281 if (cb) 3282 *cp = '\0'; 3283 3284 return s; 3285} 3286 3287/* 3288 * cookedprint - output variables in cooked mode 3289 */ 3290static void 3291cookedprint( 3292 int datatype, 3293 size_t length, 3294 const char *data, 3295 int status, 3296 int quiet, 3297 FILE *fp 3298 ) 3299{ 3300 char *name; 3301 char *value; 3302 char output_raw; 3303 int fmt; 3304 l_fp lfp; 3305 sockaddr_u hval; 3306 u_long uval; 3307 int narr; 3308 size_t len; 3309 l_fp lfparr[8]; 3310 char b[12]; 3311 char bn[2 * MAXVARLEN]; 3312 char bv[2 * MAXVALLEN]; 3313 3314 UNUSED_ARG(datatype); 3315 3316 if (!quiet) 3317 fprintf(fp, "status=%04x %s,\n", status, 3318 statustoa(datatype, status)); 3319 3320 startoutput(); 3321 while (nextvar(&length, &data, &name, &value)) { 3322 fmt = varfmt(name); 3323 output_raw = 0; 3324 switch (fmt) { 3325 3326 case PADDING: 3327 output_raw = '*'; 3328 break; 3329 3330 case TS: 3331 if (!decodets(value, &lfp)) 3332 output_raw = '?'; 3333 else 3334 output(fp, name, prettydate(&lfp)); 3335 break; 3336 3337 case HA: /* fallthru */ 3338 case NA: 3339 if (!decodenetnum(value, &hval)) { 3340 output_raw = '?'; 3341 } else if (fmt == HA){ 3342 output(fp, name, nntohost(&hval)); 3343 } else { 3344 output(fp, name, stoa(&hval)); 3345 } 3346 break; 3347 3348 case RF: 3349 if (decodenetnum(value, &hval)) { 3350 if (ISREFCLOCKADR(&hval)) 3351 output(fp, name, 3352 refnumtoa(&hval)); 3353 else 3354 output(fp, name, stoa(&hval)); 3355 } else if (strlen(value) <= 4) { 3356 output(fp, name, value); 3357 } else { 3358 output_raw = '?'; 3359 } 3360 break; 3361 3362 case LP: 3363 if (!decodeuint(value, &uval) || uval > 3) { 3364 output_raw = '?'; 3365 } else { 3366 b[0] = (0x2 & uval) 3367 ? '1' 3368 : '0'; 3369 b[1] = (0x1 & uval) 3370 ? '1' 3371 : '0'; 3372 b[2] = '\0'; 3373 output(fp, name, b); 3374 } 3375 break; 3376 3377 case OC: 3378 if (!decodeuint(value, &uval)) { 3379 output_raw = '?'; 3380 } else { 3381 snprintf(b, sizeof(b), "%03lo", uval); 3382 output(fp, name, b); 3383 } 3384 break; 3385 3386 case AR: 3387 if (!decodearr(value, &narr, lfparr)) 3388 output_raw = '?'; 3389 else 3390 outputarr(fp, name, narr, lfparr); 3391 break; 3392 3393 case FX: 3394 if (!decodeuint(value, &uval)) 3395 output_raw = '?'; 3396 else 3397 output(fp, name, tstflags(uval)); 3398 break; 3399 3400 default: 3401 fprintf(stderr, "Internal error in cookedprint, %s=%s, fmt %d\n", 3402 name, value, fmt); 3403 output_raw = '?'; 3404 break; 3405 } 3406 3407 if (output_raw != 0) { 3408 /* TALOS-CAN-0063: avoid buffer overrun */ 3409 atoascii(name, MAXVARLEN, bn, sizeof(bn)); 3410 if (output_raw != '*') { 3411 atoascii(value, MAXVALLEN, 3412 bv, sizeof(bv) - 1); 3413 len = strlen(bv); 3414 bv[len] = output_raw; 3415 bv[len+1] = '\0'; 3416 } else { 3417 atoascii(value, MAXVALLEN, 3418 bv, sizeof(bv)); 3419 } 3420 output(fp, bn, bv); 3421 } 3422 } 3423 endoutput(fp); 3424} 3425 3426 3427/* 3428 * sortassoc - sort associations in the cache into ascending order 3429 */ 3430void 3431sortassoc(void) 3432{ 3433 if (numassoc > 1) 3434 qsort(assoc_cache, (size_t)numassoc, 3435 sizeof(assoc_cache[0]), &assoccmp); 3436} 3437 3438 3439/* 3440 * assoccmp - compare two associations 3441 */ 3442static int 3443assoccmp( 3444 const void *t1, 3445 const void *t2 3446 ) 3447{ 3448 const struct association *ass1 = t1; 3449 const struct association *ass2 = t2; 3450 3451 if (ass1->assid < ass2->assid) 3452 return -1; 3453 if (ass1->assid > ass2->assid) 3454 return 1; 3455 return 0; 3456} 3457 3458 3459/* 3460 * grow_assoc_cache() - enlarge dynamic assoc_cache array 3461 * 3462 * The strategy is to add an assumed 4k page size at a time, leaving 3463 * room for malloc() bookkeeping overhead equivalent to 4 pointers. 3464 */ 3465void 3466grow_assoc_cache(void) 3467{ 3468 static size_t prior_sz; 3469 size_t new_sz; 3470 3471 new_sz = prior_sz + 4 * 1024; 3472 if (0 == prior_sz) { 3473 new_sz -= 4 * sizeof(void *); 3474 } 3475 assoc_cache = erealloc_zero(assoc_cache, new_sz, prior_sz); 3476 prior_sz = new_sz; 3477 assoc_cache_slots = (u_int)(new_sz / sizeof(assoc_cache[0])); 3478} 3479 3480 3481/* 3482 * ntpq_custom_opt_handler - autoopts handler for -c and -p 3483 * 3484 * By default, autoopts loses the relative order of -c and -p options 3485 * on the command line. This routine replaces the default handler for 3486 * those routines and builds a list of commands to execute preserving 3487 * the order. 3488 */ 3489void 3490ntpq_custom_opt_handler( 3491 tOptions *pOptions, 3492 tOptDesc *pOptDesc 3493 ) 3494{ 3495 switch (pOptDesc->optValue) { 3496 3497 default: 3498 fprintf(stderr, 3499 "ntpq_custom_opt_handler unexpected option '%c' (%d)\n", 3500 pOptDesc->optValue, pOptDesc->optValue); 3501 exit(1); 3502 3503 case 'c': 3504 ADDCMD(pOptDesc->pzLastArg); 3505 break; 3506 3507 case 'p': 3508 ADDCMD("peers"); 3509 break; 3510 } 3511} 3512/* 3513 * Obtain list of digest names 3514 */ 3515 3516#ifdef OPENSSL 3517# ifdef HAVE_EVP_MD_DO_ALL_SORTED 3518struct hstate { 3519 char *list; 3520 const char **seen; 3521 int idx; 3522}; 3523#define K_PER_LINE 8 3524#define K_NL_PFX_STR "\n " 3525#define K_DELIM_STR ", " 3526static void list_md_fn(const EVP_MD *m, const char *from, const char *to, void *arg ) 3527{ 3528 size_t len, n; 3529 const char *name, *cp, **seen; 3530 struct hstate *hstate = arg; 3531 EVP_MD_CTX ctx; 3532 u_int digest_len; 3533 u_char digest[EVP_MAX_MD_SIZE]; 3534 3535 if (!m) 3536 return; /* Ignore aliases */ 3537 3538 name = EVP_MD_name(m); 3539 3540 /* Lowercase names aren't accepted by keytype_from_text in ssl_init.c */ 3541 3542 for( cp = name; *cp; cp++ ) { 3543 if( islower(*cp) ) 3544 return; 3545 } 3546 len = (cp - name) + 1; 3547 3548 /* There are duplicates. Discard if name has been seen. */ 3549 3550 for (seen = hstate->seen; *seen; seen++) 3551 if (!strcmp(*seen, name)) 3552 return; 3553 n = (seen - hstate->seen) + 2; 3554 hstate->seen = erealloc(hstate->seen, n * sizeof(*seen)); 3555 hstate->seen[n-2] = name; 3556 hstate->seen[n-1] = NULL; 3557 3558 /* Discard MACs that NTP won't accept. 3559 * Keep this consistent with keytype_from_text() in ssl_init.c. 3560 */ 3561 3562 EVP_DigestInit(&ctx, EVP_get_digestbyname(name)); 3563 EVP_DigestFinal(&ctx, digest, &digest_len); 3564 if (digest_len > (MAX_MAC_LEN - sizeof(keyid_t))) 3565 return; 3566 3567 if (hstate->list != NULL) 3568 len += strlen(hstate->list); 3569 len += (hstate->idx >= K_PER_LINE)? strlen(K_NL_PFX_STR): strlen(K_DELIM_STR); 3570 3571 if (hstate->list == NULL) { 3572 hstate->list = (char *)emalloc(len); 3573 hstate->list[0] = '\0'; 3574 } else 3575 hstate->list = (char *)erealloc(hstate->list, len); 3576 3577 sprintf(hstate->list + strlen(hstate->list), "%s%s", 3578 ((hstate->idx >= K_PER_LINE)? K_NL_PFX_STR : K_DELIM_STR), 3579 name); 3580 if (hstate->idx >= K_PER_LINE) 3581 hstate->idx = 1; 3582 else 3583 hstate->idx++; 3584} 3585# endif 3586#endif 3587 3588static char *list_digest_names(void) 3589{ 3590 char *list = NULL; 3591 3592#ifdef OPENSSL 3593# ifdef HAVE_EVP_MD_DO_ALL_SORTED 3594 struct hstate hstate = { NULL, NULL, K_PER_LINE+1 }; 3595 3596 hstate.seen = (const char **) emalloc_zero(1*sizeof( const char * )); // replaces -> calloc(1, sizeof( const char * )); 3597 3598 INIT_SSL(); 3599 EVP_MD_do_all_sorted(list_md_fn, &hstate); 3600 list = hstate.list; 3601 free(hstate.seen); 3602# else 3603 list = (char *)emalloc(sizeof("md5, others (upgrade to OpenSSL-1.0 for full list)")); 3604 strcpy(list, "md5, others (upgrade to OpenSSL-1.0 for full list)"); 3605# endif 3606#else 3607 list = (char *)emalloc(sizeof("md5")); 3608 strcpy(list, "md5"); 3609#endif 3610 3611 return list; 3612} 3613 3614#define CTRLC_STACK_MAX 4 3615static volatile size_t ctrlc_stack_len = 0; 3616static volatile Ctrl_C_Handler ctrlc_stack[CTRLC_STACK_MAX]; 3617 3618 3619 3620int/*BOOL*/ 3621push_ctrl_c_handler( 3622 Ctrl_C_Handler func 3623 ) 3624{ 3625 size_t size = ctrlc_stack_len; 3626 if (func && (size < CTRLC_STACK_MAX)) { 3627 ctrlc_stack[size] = func; 3628 ctrlc_stack_len = size + 1; 3629 return TRUE; 3630 } 3631 return FALSE; 3632} 3633 3634int/*BOOL*/ 3635pop_ctrl_c_handler( 3636 Ctrl_C_Handler func 3637 ) 3638{ 3639 size_t size = ctrlc_stack_len; 3640 if (size) { 3641 --size; 3642 if (func == NULL || func == ctrlc_stack[size]) { 3643 ctrlc_stack_len = size; 3644 return TRUE; 3645 } 3646 } 3647 return FALSE; 3648} 3649 3650static void 3651on_ctrlc(void) 3652{ 3653 size_t size = ctrlc_stack_len; 3654 while (size) 3655 if ((*ctrlc_stack[--size])()) 3656 break; 3657} 3658 3659static int 3660my_easprintf( 3661 char ** ppinto, 3662 const char * fmt , 3663 ... 3664 ) 3665{ 3666 va_list va; 3667 int prc; 3668 size_t len = 128; 3669 char * buf = emalloc(len); 3670 3671 again: 3672 /* Note: we expect the memory allocation to fail long before the 3673 * increment in buffer size actually overflows. 3674 */ 3675 buf = (buf) ? erealloc(buf, len) : emalloc(len); 3676 3677 va_start(va, fmt); 3678 prc = vsnprintf(buf, len, fmt, va); 3679 va_end(va); 3680 3681 if (prc < 0) { 3682 /* might be very old vsnprintf. Or actually MSVC... */ 3683 len += len >> 1; 3684 goto again; 3685 } 3686 if ((size_t)prc >= len) { 3687 /* at least we have the proper size now... */ 3688 len = (size_t)prc + 1; 3689 goto again; 3690 } 3691 if ((size_t)prc < (len - 32)) 3692 buf = erealloc(buf, (size_t)prc + 1); 3693 *ppinto = buf; 3694 return prc; 3695} 3696