PageRenderTime 123ms CodeModel.GetById 23ms app.highlight 84ms RepoModel.GetById 1ms app.codeStats 1ms

/contrib/ntp/ntpq/ntpq.c

https://bitbucket.org/freebsd/freebsd-head/
C | 3293 lines | 2565 code | 297 blank | 431 comment | 550 complexity | 85cdcabf82d1b317a75154a79945a7bb MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2 * ntpq - query an NTP server using mode 6 commands
   3 */
   4
   5#include <stdio.h>
   6
   7#include <ctype.h>
   8#include <signal.h>
   9#include <setjmp.h>
  10#include <sys/types.h>
  11#include <sys/time.h>
  12
  13#include "ntpq.h"
  14#include "ntp_unixtime.h"
  15#include "ntp_calendar.h"
  16#include "ntp_io.h"
  17#include "ntp_select.h"
  18#include "ntp_stdlib.h"
  19/* Don't include ISC's version of IPv6 variables and structures */
  20#define ISC_IPV6_H 1
  21#include "isc/net.h"
  22#include "isc/result.h"
  23
  24#include "ntpq-opts.h"
  25
  26#ifdef SYS_WINNT
  27# include <Mswsock.h>
  28# include <io.h>
  29#else
  30# define closesocket close
  31#endif /* SYS_WINNT */
  32
  33#if defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDIT)
  34# include <readline/readline.h>
  35# include <readline/history.h>
  36#endif /* HAVE_LIBREADLINE || HAVE_LIBEDIT */
  37
  38#ifdef SYS_VXWORKS
  39				/* vxWorks needs mode flag -casey*/
  40# define open(name, flags)   open(name, flags, 0777)
  41# define SERVER_PORT_NUM     123
  42#endif
  43
  44/* we use COMMAND as an autogen keyword */
  45#ifdef COMMAND
  46# undef COMMAND
  47#endif
  48
  49/*
  50 * Because we potentially understand a lot of commands we will run
  51 * interactive if connected to a terminal.
  52 */
  53int interactive = 0;		/* set to 1 when we should prompt */
  54const char *prompt = "ntpq> ";	/* prompt to ask him about */
  55
  56
  57/*
  58 * for get_systime()
  59 */
  60s_char	sys_precision;		/* local clock precision (log2 s) */
  61
  62/*
  63 * Keyid used for authenticated requests.  Obtained on the fly.
  64 */
  65u_long info_auth_keyid = 0;
  66
  67/*
  68 * Type of key md5
  69 */
  70#define	KEY_TYPE_MD5	4
  71
  72static	int info_auth_keytype = KEY_TYPE_MD5;	/* MD5 */
  73u_long	current_time;		/* needed by authkeys; not used */
  74
  75/*
  76 * Flag which indicates we should always send authenticated requests
  77 */
  78int always_auth = 0;
  79
  80/*
  81 * Flag which indicates raw mode output.
  82 */
  83int rawmode = 0;
  84
  85/*
  86 * Packet version number we use
  87 */
  88u_char pktversion = NTP_OLDVERSION + 1;
  89
  90/*
  91 * Don't jump if no set jmp.
  92 */
  93volatile int jump = 0;
  94
  95/*
  96 * Format values
  97 */
  98#define	PADDING	0
  99#define	TS	1	/* time stamp */
 100#define	FL	2	/* l_fp type value */
 101#define	FU	3	/* u_fp type value */
 102#define	FS	4	/* s_fp type value */
 103#define	UI	5	/* unsigned integer value */
 104#define	SI	6	/* signed integer value */
 105#define	HA	7	/* host address */
 106#define	NA	8	/* network address */
 107#define	ST	9	/* string value */
 108#define	RF	10	/* refid (sometimes string, sometimes not) */
 109#define	LP	11	/* leap (print in binary) */
 110#define	OC	12	/* integer, print in octal */
 111#define	MD	13	/* mode */
 112#define	AR	14	/* array of times */
 113#define FX	15	/* test flags */
 114#define	EOV	255	/* end of table */
 115
 116
 117/*
 118 * System variable values.  The array can be indexed by
 119 * the variable index to find the textual name.
 120 */
 121struct ctl_var sys_var[] = {
 122	{ 0,		PADDING, "" },		/* 0 */
 123	{ CS_LEAP,	LP,	"leap" },	/* 1 */
 124	{ CS_STRATUM,	UI,	"stratum" },	/* 2 */
 125	{ CS_PRECISION,	SI,	"precision" },	/* 3 */
 126	{ CS_ROOTDELAY,	FS,	"rootdelay" },	/* 4 */
 127	{ CS_ROOTDISPERSION, FU, "rootdispersion" }, /* 5 */
 128	{ CS_REFID,	RF,	"refid" },	/* 6 */
 129	{ CS_REFTIME,	TS,	"reftime" },	/* 7 */
 130	{ CS_POLL,	UI,	"poll" },	/* 8 */
 131	{ CS_PEERID,	UI,	"peer" },	/* 9 */
 132	{ CS_STATE,	UI,	"state" },	/* 10 */
 133	{ CS_OFFSET,	FL,	"offset" },	/* 11 */
 134	{ CS_DRIFT,	FS,	"frequency" },	/* 12 */
 135	{ CS_JITTER,	FU,	"jitter" },	/* 13 */
 136	{ CS_CLOCK,	TS,	"clock" },	/* 14 */
 137	{ CS_PROCESSOR,	ST,	"processor" },	/* 15 */
 138	{ CS_SYSTEM,	ST,	"system" },	/* 16 */
 139	{ CS_VERSION,	ST,	"version" },	/* 17 */
 140	{ CS_STABIL,	FS,	"stability" },	/* 18 */
 141	{ CS_VARLIST,	ST,	"sys_var_list" }, /* 19 */
 142	{ 0,		EOV,	""	}
 143};
 144
 145
 146/*
 147 * Peer variable list
 148 */
 149struct ctl_var peer_var[] = {
 150	{ 0,		PADDING, "" },		/* 0 */
 151	{ CP_CONFIG,	UI,	"config" },	/* 1 */
 152	{ CP_AUTHENABLE, UI,	"authenable" },	/* 2 */
 153	{ CP_AUTHENTIC,	UI,	"authentic" },	/* 3 */
 154	{ CP_SRCADR,	HA,	"srcadr" },	/* 4 */
 155	{ CP_SRCPORT,	UI,	"srcport" },	/* 5 */
 156	{ CP_DSTADR,	NA,	"dstadr" },	/* 6 */
 157	{ CP_DSTPORT,	UI,	"dstport" },	/* 7 */
 158	{ CP_LEAP,	LP,	"leap" },	/* 8 */
 159	{ CP_HMODE,	MD,	"hmode" },	/* 9 */
 160	{ CP_STRATUM,	UI,	"stratum" },	/* 10 */
 161	{ CP_PPOLL,	UI,	"ppoll" },	/* 11 */
 162	{ CP_HPOLL,	UI,	"hpoll" },	/* 12 */
 163	{ CP_PRECISION,	SI,	"precision" },	/* 13 */
 164	{ CP_ROOTDELAY,	FS,	"rootdelay" },	/* 14 */
 165	{ CP_ROOTDISPERSION, FU, "rootdispersion" }, /* 15 */
 166	{ CP_REFID,	RF,	"refid" },	/* 16 */
 167	{ CP_REFTIME,	TS,	"reftime" },	/* 17 */
 168	{ CP_ORG,	TS,	"org" },	/* 18 */
 169	{ CP_REC,	TS,	"rec" },	/* 19 */
 170	{ CP_XMT,	TS,	"xmt" },	/* 20 */
 171	{ CP_REACH,	OC,	"reach" },	/* 21 */
 172	{ CP_UNREACH,	UI,	"unreach" },	/* 22 */
 173	{ CP_TIMER,	UI,	"timer" },	/* 23 */
 174	{ CP_DELAY,	FS,	"delay" },	/* 24 */
 175	{ CP_OFFSET,	FL,	"offset" },	/* 25 */
 176	{ CP_JITTER,	FU,	"jitter" },	/* 26 */
 177	{ CP_DISPERSION, FU,	"dispersion" },	/* 27 */
 178	{ CP_KEYID,	UI,	"keyid" },	/* 28 */
 179	{ CP_FILTDELAY,	AR,	"filtdelay" },	/* 29 */
 180	{ CP_FILTOFFSET, AR,	"filtoffset" },	/* 30 */
 181	{ CP_PMODE,	ST,	"pmode" },	/* 31 */
 182	{ CP_RECEIVED,	UI,	"received" },	/* 32 */
 183	{ CP_SENT,	UI,	"sent" },	/* 33 */
 184	{ CP_FILTERROR,	AR,	"filtdisp" },	/* 34 */
 185	{ CP_FLASH,     FX,	"flash" },	/* 35 */ 
 186	{ CP_TTL,	UI,	"ttl" },	/* 36 */
 187	/*
 188	 * These are duplicate entries so that we can
 189	 * process deviant version of the ntp protocol.
 190	 */
 191	{ CP_SRCADR,	HA,	"peeraddr" },	/* 4 */
 192	{ CP_SRCPORT,	UI,	"peerport" },	/* 5 */
 193	{ CP_PPOLL,	UI,	"peerpoll" },	/* 11 */
 194	{ CP_HPOLL,	UI,	"hostpoll" },	/* 12 */
 195	{ CP_FILTERROR,	AR,	"filterror" },	/* 34 */
 196	{ 0,		EOV,	""	}
 197};
 198
 199
 200/*
 201 * Clock variable list
 202 */
 203struct ctl_var clock_var[] = {
 204	{ 0,		PADDING, "" },		/* 0 */
 205	{ CC_TYPE,	UI,	"type" },	/* 1 */
 206	{ CC_TIMECODE,	ST,	"timecode" },	/* 2 */
 207	{ CC_POLL,	UI,	"poll" },	/* 3 */
 208	{ CC_NOREPLY,	UI,	"noreply" },	/* 4 */
 209	{ CC_BADFORMAT,	UI,	"badformat" },	/* 5 */
 210	{ CC_BADDATA,	UI,	"baddata" },	/* 6 */
 211	{ CC_FUDGETIME1, FL,	"fudgetime1" },	/* 7 */
 212	{ CC_FUDGETIME2, FL,	"fudgetime2" },	/* 8 */
 213	{ CC_FUDGEVAL1,	UI,	"stratum" },	/* 9 */
 214	{ CC_FUDGEVAL2,	RF,	"refid" },	/* 10 */
 215	{ CC_FLAGS,	UI,	"flags" },	/* 11 */
 216	{ CC_DEVICE,	ST,	"device" },	/* 12 */
 217	{ 0,		EOV,	""	}
 218};
 219
 220
 221/*
 222 * flasher bits
 223 */
 224static const char *tstflagnames[] = {
 225	"pkt_dup",		/* TEST1 */
 226	"pkt_bogus",		/* TEST2 */
 227	"pkt_proto",		/* TEST3 */
 228	"pkt_denied",		/* TEST4 */
 229	"pkt_auth",		/* TEST5 */
 230	"pkt_synch",		/* TEST6 */
 231	"pkt_dist",		/* TEST7 */
 232	"pkt_autokey",		/* TEST8 */
 233	"pkt_crypto",		/* TEST9 */
 234	"peer_stratum",		/* TEST10 */
 235	"peer_dist",		/* TEST11 */
 236	"peer_loop",		/* TEST12 */
 237	"peer_unfit"		/* TEST13 */
 238};
 239
 240
 241int		ntpqmain	P((int,	char **));
 242/*
 243 * Built in command handler declarations
 244 */
 245static	int	openhost	P((const char *));
 246static	int	sendpkt		P((char *, int));
 247static	int	getresponse	P((int, int, u_short *, int *, char **, int));
 248static	int	sendrequest	P((int, int, int, int, char *));
 249static	char *	tstflags	P((u_long));
 250static	void	getcmds		P((void));
 251static	RETSIGTYPE abortcmd	P((int));
 252static	void	docmd		P((const char *));
 253static	void	tokenize	P((const char *, char **, int *));
 254static	int	findcmd		P((char *, struct xcmd *, struct xcmd *, struct xcmd **));
 255static	int	getarg		P((char *, int, arg_v *));
 256static	int	rtdatetolfp	P((char *, l_fp *));
 257static	int	decodearr	P((char *, int *, l_fp *));
 258static	void	help		P((struct parse *, FILE *));
 259#ifdef QSORT_USES_VOID_P
 260static	int	helpsort	P((const void *, const void *));
 261#else
 262static	int	helpsort	P((char **, char **));
 263#endif
 264static	void	printusage	P((struct xcmd *, FILE *));
 265static	void	timeout		P((struct parse *, FILE *));
 266static	void	auth_delay	P((struct parse *, FILE *));
 267static	void	host		P((struct parse *, FILE *));
 268static	void	ntp_poll	P((struct parse *, FILE *));
 269static	void	keyid		P((struct parse *, FILE *));
 270static	void	keytype		P((struct parse *, FILE *));
 271static	void	passwd		P((struct parse *, FILE *));
 272static	void	hostnames	P((struct parse *, FILE *));
 273static	void	setdebug	P((struct parse *, FILE *));
 274static	void	quit		P((struct parse *, FILE *));
 275static	void	version		P((struct parse *, FILE *));
 276static	void	raw		P((struct parse *, FILE *));
 277static	void	cooked		P((struct parse *, FILE *));
 278static	void	authenticate	P((struct parse *, FILE *));
 279static	void	ntpversion	P((struct parse *, FILE *));
 280static	void	warning		P((const char *, const char *, const char *));
 281static	void	error		P((const char *, const char *, const char *));
 282static	u_long	getkeyid	P((const char *));
 283static	void	atoascii	P((int, char *, char *));
 284static	void	makeascii	P((int, char *, FILE *));
 285static	void	rawprint	P((int, int, char *, int, FILE *));
 286static	void	startoutput	P((void));
 287static	void	output		P((FILE *, char *, char *));
 288static	void	endoutput	P((FILE *));
 289static	void	outputarr	P((FILE *, char *, int, l_fp *));
 290static	void	cookedprint	P((int, int, char *, int, FILE *));
 291#ifdef QSORT_USES_VOID_P
 292static	int	assoccmp	P((const void *, const void *));
 293#else
 294static	int	assoccmp	P((struct association *, struct association *));
 295#endif /* sgi || bsdi */
 296
 297
 298/*
 299 * Built-in commands we understand
 300 */
 301struct xcmd builtins[] = {
 302	{ "?",		help,		{  OPT|NTP_STR, NO, NO, NO },
 303	  { "command", "", "", "" },
 304	  "tell the use and syntax of commands" },
 305	{ "help",	help,		{  OPT|NTP_STR, NO, NO, NO },
 306	  { "command", "", "", "" },
 307	  "tell the use and syntax of commands" },
 308	{ "timeout",	timeout,	{ OPT|NTP_UINT, NO, NO, NO },
 309	  { "msec", "", "", "" },
 310	  "set the primary receive time out" },
 311	{ "delay",	auth_delay,	{ OPT|NTP_INT, NO, NO, NO },
 312	  { "msec", "", "", "" },
 313	  "set the delay added to encryption time stamps" },
 314	{ "host",	host,		{ OPT|NTP_STR, OPT|NTP_STR, NO, NO },
 315	  { "-4|-6", "hostname", "", "" },
 316	  "specify the host whose NTP server we talk to" },
 317	{ "poll",	ntp_poll,	{ OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
 318	  { "n", "verbose", "", "" },
 319	  "poll an NTP server in client mode `n' times" },
 320	{ "passwd",	passwd,		{ NO, NO, NO, NO },
 321	  { "", "", "", "" },
 322	  "specify a password to use for authenticated requests"},
 323	{ "hostnames",	hostnames,	{ OPT|NTP_STR, NO, NO, NO },
 324	  { "yes|no", "", "", "" },
 325	  "specify whether hostnames or net numbers are printed"},
 326	{ "debug",	setdebug,	{ OPT|NTP_STR, NO, NO, NO },
 327	  { "no|more|less", "", "", "" },
 328	  "set/change debugging level" },
 329	{ "quit",	quit,		{ NO, NO, NO, NO },
 330	  { "", "", "", "" },
 331	  "exit ntpq" },
 332	{ "exit",	quit,		{ NO, NO, NO, NO },
 333	  { "", "", "", "" },
 334	  "exit ntpq" },
 335	{ "keyid",	keyid,		{ OPT|NTP_UINT, NO, NO, NO },
 336	  { "key#", "", "", "" },
 337	  "set keyid to use for authenticated requests" },
 338	{ "version",	version,	{ NO, NO, NO, NO },
 339	  { "", "", "", "" },
 340	  "print version number" },
 341	{ "raw",	raw,		{ NO, NO, NO, NO },
 342	  { "", "", "", "" },
 343	  "do raw mode variable output" },
 344	{ "cooked",	cooked,		{ NO, NO, NO, NO },
 345	  { "", "", "", "" },
 346	  "do cooked mode variable output" },
 347	{ "authenticate", authenticate,	{ OPT|NTP_STR, NO, NO, NO },
 348	  { "yes|no", "", "", "" },
 349	  "always authenticate requests to this server" },
 350	{ "ntpversion",	ntpversion,	{ OPT|NTP_UINT, NO, NO, NO },
 351	  { "version number", "", "", "" },
 352	  "set the NTP version number to use for requests" },
 353	{ "keytype",	keytype,	{ OPT|NTP_STR, NO, NO, NO },
 354	  { "key type (md5|des)", "", "", "" },
 355	  "set key type to use for authenticated requests (des|md5)" },
 356	{ 0,		0,		{ NO, NO, NO, NO },
 357	  { "", "", "", "" }, "" }
 358};
 359
 360
 361/*
 362 * Default values we use.
 363 */
 364#define	DEFTIMEOUT	(5)		/* 5 second time out */
 365#define	DEFSTIMEOUT	(2)		/* 2 second time out after first */
 366#define	DEFDELAY	0x51EB852	/* 20 milliseconds, l_fp fraction */
 367#define	DEFHOST		"localhost"	/* default host name */
 368#define	LENHOSTNAME	256		/* host name is 256 characters long */
 369#define	MAXCMDS		100		/* maximum commands on cmd line */
 370#define	MAXHOSTS	200		/* maximum hosts on cmd line */
 371#define	MAXLINE		512		/* maximum line length */
 372#define	MAXTOKENS	(1+MAXARGS+2)	/* maximum number of usable tokens */
 373#define	MAXVARLEN	256		/* maximum length of a variable name */
 374#define	MAXVALLEN	400		/* maximum length of a variable value */
 375#define	MAXOUTLINE	72		/* maximum length of an output line */
 376#define SCREENWIDTH     76              /* nominal screen width in columns */
 377
 378/*
 379 * Some variables used and manipulated locally
 380 */
 381struct timeval tvout = { DEFTIMEOUT, 0 };	/* time out for reads */
 382struct timeval tvsout = { DEFSTIMEOUT, 0 };	/* secondary time out */
 383l_fp delay_time;				/* delay time */
 384char currenthost[LENHOSTNAME];			/* current host name */
 385struct sockaddr_in hostaddr = { 0 };		/* host address */
 386int showhostnames = 1;				/* show host names by default */
 387
 388int ai_fam_templ;				/* address family */
 389int ai_fam_default;				/* default address family */
 390SOCKET sockfd;					/* fd socket is opened on */
 391int havehost = 0;				/* set to 1 when host open */
 392int s_port = 0;
 393struct servent *server_entry = NULL;		/* server entry for ntp */
 394
 395#ifdef SYS_WINNT
 396DWORD NumberOfBytesWritten;
 397
 398HANDLE	TimerThreadHandle = NULL;	/* 1998/06/03 - Used in ntplib/machines.c */
 399void timer(void)	{  ; };	/* 1998/06/03 - Used in ntplib/machines.c */
 400
 401#endif /* SYS_WINNT */
 402
 403/*
 404 * Sequence number used for requests.  It is incremented before
 405 * it is used.
 406 */
 407u_short sequence;
 408
 409/*
 410 * Holds data returned from queries.  Declare buffer long to be sure of
 411 * alignment.
 412 */
 413#define	MAXFRAGS	24		/* maximum number of fragments */
 414#define	DATASIZE	(MAXFRAGS*480)	/* maximum amount of data */
 415long pktdata[DATASIZE/sizeof(long)];
 416
 417/*
 418 * Holds association data for use with the &n operator.
 419 */
 420struct association assoc_cache[MAXASSOC];
 421int numassoc = 0;		/* number of cached associations */
 422
 423/*
 424 * For commands typed on the command line (with the -c option)
 425 */
 426int numcmds = 0;
 427const char *ccmds[MAXCMDS];
 428#define	ADDCMD(cp)	if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
 429
 430/*
 431 * When multiple hosts are specified.
 432 */
 433int numhosts = 0;
 434const char *chosts[MAXHOSTS];
 435#define	ADDHOST(cp)	if (numhosts < MAXHOSTS) chosts[numhosts++] = (cp)
 436
 437/*
 438 * Error codes for internal use
 439 */
 440#define	ERR_UNSPEC		256
 441#define	ERR_INCOMPLETE	257
 442#define	ERR_TIMEOUT		258
 443#define	ERR_TOOMUCH		259
 444
 445/*
 446 * Macro definitions we use
 447 */
 448#define	ISSPACE(c)	((c) == ' ' || (c) == '\t')
 449#define	ISEOL(c)	((c) == '\n' || (c) == '\r' || (c) == '\0')
 450#define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
 451
 452/*
 453 * Jump buffer for longjumping back to the command level
 454 */
 455jmp_buf interrupt_buf;
 456
 457/*
 458 * Points at file being currently printed into
 459 */
 460FILE *current_output;
 461
 462/*
 463 * Command table imported from ntpdc_ops.c
 464 */
 465extern struct xcmd opcmds[];
 466
 467char *progname;
 468volatile int debug;
 469
 470#ifdef NO_MAIN_ALLOWED
 471CALL(ntpq,"ntpq",ntpqmain);
 472
 473void clear_globals(void)
 474{
 475    extern int ntp_optind;
 476    showhostnames = 0;				/* don'tshow host names by default */
 477    ntp_optind = 0;
 478    server_entry = NULL;            /* server entry for ntp */
 479    havehost = 0;				/* set to 1 when host open */
 480    numassoc = 0;		/* number of cached associations */
 481    numcmds = 0;
 482    numhosts = 0;
 483}
 484#endif
 485
 486/*
 487 * main - parse arguments and handle options
 488 */
 489#ifndef NO_MAIN_ALLOWED
 490int
 491main(
 492	int argc,
 493	char *argv[]
 494	)
 495{
 496	return ntpqmain(argc, argv);
 497}
 498#endif
 499
 500int
 501ntpqmain(
 502	int argc,
 503	char *argv[]
 504	)
 505{
 506	extern int ntp_optind;
 507
 508#ifdef SYS_VXWORKS
 509	clear_globals();
 510	taskPrioritySet(taskIdSelf(), 100 );
 511#endif
 512
 513	delay_time.l_ui = 0;
 514	delay_time.l_uf = DEFDELAY;
 515
 516#ifdef SYS_WINNT
 517	if (!Win32InitSockets())
 518	{
 519		fprintf(stderr, "No useable winsock.dll:");
 520		exit(1);
 521	}
 522#endif /* SYS_WINNT */
 523
 524	/* Check to see if we have IPv6. Otherwise force the -4 flag */
 525	if (isc_net_probeipv6() != ISC_R_SUCCESS) {
 526		ai_fam_default = AF_INET;
 527	}
 528
 529	progname = argv[0];
 530
 531	{
 532		int optct = optionProcess(&ntpqOptions, argc, argv);
 533		argc -= optct;
 534		argv += optct;
 535	}
 536
 537	switch (WHICH_IDX_IPV4) {
 538	    case INDEX_OPT_IPV4:
 539		ai_fam_templ = AF_INET;
 540		break;
 541	    case INDEX_OPT_IPV6:
 542		ai_fam_templ = AF_INET6;
 543		break;
 544	    default:
 545		ai_fam_templ = ai_fam_default;
 546		break;
 547	}
 548
 549	if (HAVE_OPT(COMMAND)) {
 550		int		cmdct = STACKCT_OPT( COMMAND );
 551		const char**	cmds  = STACKLST_OPT( COMMAND );
 552
 553		while (cmdct-- > 0) {
 554			ADDCMD(*cmds++);
 555		}
 556	}
 557
 558	debug = DESC(DEBUG_LEVEL).optOccCt;
 559
 560	if (HAVE_OPT(INTERACTIVE)) {
 561		interactive = 1;
 562	}
 563
 564	if (HAVE_OPT(NUMERIC)) {
 565		showhostnames = 0;
 566	}
 567
 568	if (HAVE_OPT(PEERS)) {
 569		ADDCMD("peers");
 570	}
 571
 572#if 0
 573	while ((c = ntp_getopt(argc, argv, "46c:dinp")) != EOF)
 574	    switch (c) {
 575		case '4':
 576		    ai_fam_templ = AF_INET;
 577		    break;
 578		case '6':
 579		    ai_fam_templ = AF_INET6;
 580		    break;
 581		case 'c':
 582		    ADDCMD(ntp_optarg);
 583		    break;
 584		case 'd':
 585		    ++debug;
 586		    break;
 587		case 'i':
 588		    interactive = 1;
 589		    break;
 590		case 'n':
 591		    showhostnames = 0;
 592		    break;
 593		case 'p':
 594		    ADDCMD("peers");
 595		    break;
 596		default:
 597		    errflg++;
 598		    break;
 599	    }
 600	if (errflg) {
 601		(void) fprintf(stderr,
 602			       "usage: %s [-46dinp] [-c cmd] host ...\n",
 603			       progname);
 604		exit(2);
 605	}
 606#endif
 607	if (ntp_optind == argc) {
 608		ADDHOST(DEFHOST);
 609	} else {
 610		for (; ntp_optind < argc; ntp_optind++)
 611		    ADDHOST(argv[ntp_optind]);
 612	}
 613
 614	if (numcmds == 0 && interactive == 0
 615	    && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
 616		interactive = 1;
 617	}
 618
 619#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
 620	if (interactive)
 621	    (void) signal_no_reset(SIGINT, abortcmd);
 622#endif /* SYS_WINNT */
 623
 624	if (numcmds == 0) {
 625		(void) openhost(chosts[0]);
 626		getcmds();
 627	} else {
 628		int ihost;
 629		int icmd;
 630
 631		for (ihost = 0; ihost < numhosts; ihost++) {
 632			if (openhost(chosts[ihost]))
 633			    for (icmd = 0; icmd < numcmds; icmd++)
 634				docmd(ccmds[icmd]);
 635		}
 636	}
 637#ifdef SYS_WINNT
 638	WSACleanup();
 639#endif /* SYS_WINNT */
 640	return 0;
 641}
 642
 643
 644/*
 645 * openhost - open a socket to a host
 646 */
 647static int
 648openhost(
 649	const char *hname
 650	)
 651{
 652	char temphost[LENHOSTNAME];
 653	int a_info, i;
 654	struct addrinfo hints, *ai = NULL;
 655	register const char *cp;
 656	char name[LENHOSTNAME];
 657	char service[5];
 658
 659	/*
 660	 * We need to get by the [] if they were entered
 661	 */
 662	
 663	cp = hname;
 664	
 665	if(*cp == '[') {
 666		cp++;
 667		for(i = 0; *cp != ']'; cp++, i++)
 668			name[i] = *cp;
 669	name[i] = '\0';
 670	hname = name;
 671	}
 672
 673	/*
 674	 * First try to resolve it as an ip address and if that fails,
 675	 * do a fullblown (dns) lookup. That way we only use the dns
 676	 * when it is needed and work around some implementations that
 677	 * will return an "IPv4-mapped IPv6 address" address if you
 678	 * give it an IPv4 address to lookup.
 679	 */
 680	strcpy(service, "ntp");
 681	memset((char *)&hints, 0, sizeof(struct addrinfo));
 682	hints.ai_family = ai_fam_templ;
 683	hints.ai_protocol = IPPROTO_UDP;
 684	hints.ai_socktype = SOCK_DGRAM;
 685	hints.ai_flags = AI_NUMERICHOST;
 686
 687	a_info = getaddrinfo(hname, service, &hints, &ai);
 688	if (a_info == EAI_NONAME
 689#ifdef EAI_NODATA
 690	    || a_info == EAI_NODATA
 691#endif
 692	   ) {
 693		hints.ai_flags = AI_CANONNAME;
 694#ifdef AI_ADDRCONFIG
 695		hints.ai_flags |= AI_ADDRCONFIG;
 696#endif
 697		a_info = getaddrinfo(hname, service, &hints, &ai);	
 698	}
 699	/* Some older implementations don't like AI_ADDRCONFIG. */
 700	if (a_info == EAI_BADFLAGS) {
 701		hints.ai_flags = AI_CANONNAME;
 702		a_info = getaddrinfo(hname, service, &hints, &ai);	
 703	}
 704	if (a_info != 0) {
 705		(void) fprintf(stderr, "%s\n", gai_strerror(a_info));
 706		return 0;
 707	}
 708
 709	if (ai->ai_canonname == NULL) {
 710		strncpy(temphost, stoa((struct sockaddr_storage *)ai->ai_addr),
 711		    LENHOSTNAME);
 712		temphost[LENHOSTNAME-1] = '\0';
 713
 714	} else {
 715		strncpy(temphost, ai->ai_canonname, LENHOSTNAME);
 716		temphost[LENHOSTNAME-1] = '\0';
 717	}
 718
 719	if (debug > 2)
 720	    printf("Opening host %s\n", temphost);
 721
 722	if (havehost == 1) {
 723		if (debug > 2)
 724		    printf("Closing old host %s\n", currenthost);
 725		(void) closesocket(sockfd);
 726		havehost = 0;
 727	}
 728	(void) strcpy(currenthost, temphost);
 729
 730	/* port maps to the same location in both families */
 731	s_port = ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port;
 732#ifdef SYS_VXWORKS
 733	((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM);
 734	if (ai->ai_family == AF_INET)
 735		*(struct sockaddr_in *)&hostaddr=
 736			*((struct sockaddr_in *)ai->ai_addr);
 737	else
 738		*(struct sockaddr_in6 *)&hostaddr=
 739			*((struct sockaddr_in6 *)ai->ai_addr);
 740#endif /* SYS_VXWORKS */
 741
 742#ifdef SYS_WINNT
 743	{
 744		int optionValue = SO_SYNCHRONOUS_NONALERT;
 745		int err;
 746
 747		err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&optionValue, sizeof(optionValue));
 748		if (err != NO_ERROR) {
 749			(void) fprintf(stderr, "cannot open nonoverlapped sockets\n");
 750			exit(1);
 751		}
 752	}
 753#endif /* SYS_WINNT */
 754
 755	sockfd = socket(ai->ai_family, SOCK_DGRAM, 0);
 756	if (sockfd == INVALID_SOCKET) {
 757		error("socket", "", "");
 758	}
 759
 760	
 761#ifdef NEED_RCVBUF_SLOP
 762# ifdef SO_RCVBUF
 763	{ int rbufsize = DATASIZE + 2048;	/* 2K for slop */
 764	if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
 765		       &rbufsize, sizeof(int)) == -1)
 766	    error("setsockopt", "", "");
 767	}
 768# endif
 769#endif
 770
 771#ifdef SYS_VXWORKS
 772	if (connect(sockfd, (struct sockaddr *)&hostaddr,
 773		    sizeof(hostaddr)) == -1)
 774#else
 775	if (connect(sockfd, (struct sockaddr *)ai->ai_addr,
 776		    ai->ai_addrlen) == -1)
 777#endif /* SYS_VXWORKS */
 778	    error("connect", "", "");
 779	if (a_info == 0)
 780		freeaddrinfo(ai);
 781	havehost = 1;
 782	return 1;
 783}
 784
 785
 786/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
 787/*
 788 * sendpkt - send a packet to the remote host
 789 */
 790static int
 791sendpkt(
 792	char *xdata,
 793	int xdatalen
 794	)
 795{
 796	if (debug >= 3)
 797	    printf("Sending %d octets\n", xdatalen);
 798
 799
 800	if (send(sockfd, xdata, (size_t)xdatalen, 0) == -1) {
 801		warning("write to %s failed", currenthost, "");
 802		return -1;
 803	}
 804
 805	if (debug >= 4) {
 806		int first = 8;
 807		printf("Packet data:\n");
 808		while (xdatalen-- > 0) {
 809			if (first-- == 0) {
 810				printf("\n");
 811				first = 7;
 812			}
 813			printf(" %02x", *xdata++ & 0xff);
 814		}
 815		printf("\n");
 816	}
 817	return 0;
 818}
 819
 820
 821
 822/*
 823 * getresponse - get a (series of) response packet(s) and return the data
 824 */
 825static int
 826getresponse(
 827	int opcode,
 828	int associd,
 829	u_short *rstatus,
 830	int *rsize,
 831	char **rdata,
 832	int timeo
 833	)
 834{
 835	struct ntp_control rpkt;
 836	struct timeval tvo;
 837	u_short offsets[MAXFRAGS+1];
 838	u_short counts[MAXFRAGS+1];
 839	u_short offset;
 840	u_short count;
 841	int numfrags;
 842	int seenlastfrag;
 843	fd_set fds;
 844	int n;
 845
 846	/*
 847	 * This is pretty tricky.  We may get between 1 and MAXFRAG packets
 848	 * back in response to the request.  We peel the data out of
 849	 * each packet and collect it in one long block.  When the last
 850	 * packet in the sequence is received we'll know how much data we
 851	 * should have had.  Note we use one long time out, should reconsider.
 852	 */
 853	*rsize = 0;
 854	if (rstatus)
 855	    *rstatus = 0;
 856	*rdata = (char *)pktdata;
 857
 858	numfrags = 0;
 859	seenlastfrag = 0;
 860
 861	FD_ZERO(&fds);
 862
 863    again:
 864	if (numfrags == 0)
 865	    tvo = tvout;
 866	else
 867	    tvo = tvsout;
 868	
 869	FD_SET(sockfd, &fds);
 870	n = select(sockfd+1, &fds, (fd_set *)0, (fd_set *)0, &tvo);
 871
 872#if 0
 873	if (debug >= 1)
 874	    printf("select() returns %d\n", n);
 875#endif
 876
 877	if (n == -1) {
 878		warning("select fails", "", "");
 879		return -1;
 880	}
 881	if (n == 0) {
 882		/*
 883		 * Timed out.  Return what we have
 884		 */
 885		if (numfrags == 0) {
 886			if (timeo)
 887			    (void) fprintf(stderr,
 888					   "%s: timed out, nothing received\n",
 889					   currenthost);
 890			return ERR_TIMEOUT;
 891		} else {
 892			if (timeo)
 893			    (void) fprintf(stderr,
 894					   "%s: timed out with incomplete data\n",
 895					   currenthost);
 896			if (debug) {
 897				printf("Received fragments:\n");
 898				for (n = 0; n < numfrags; n++)
 899				    printf("%4d %d\n", offsets[n],
 900					   counts[n]);
 901				if (seenlastfrag)
 902				    printf("last fragment received\n");
 903				else
 904				    printf("last fragment not received\n");
 905			}
 906			return ERR_INCOMPLETE;
 907		}
 908	}
 909
 910	n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
 911	if (n == -1) {
 912		warning("read", "", "");
 913		return -1;
 914	}
 915
 916	if (debug >= 4) {
 917		int len = n, first = 8;
 918		char *data = (char *)&rpkt;
 919
 920		printf("Packet data:\n");
 921		while (len-- > 0) {
 922			if (first-- == 0) {
 923				printf("\n");
 924				first = 7;
 925			}
 926			printf(" %02x", *data++ & 0xff);
 927		}
 928		printf("\n");
 929	}
 930
 931	/*
 932	 * Check for format errors.  Bug proofing.
 933	 */
 934	if (n < CTL_HEADER_LEN) {
 935		if (debug)
 936		    printf("Short (%d byte) packet received\n", n);
 937		goto again;
 938	}
 939	if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION
 940	    || PKT_VERSION(rpkt.li_vn_mode) < NTP_OLDVERSION) {
 941		if (debug)
 942		    printf("Packet received with version %d\n",
 943			   PKT_VERSION(rpkt.li_vn_mode));
 944		goto again;
 945	}
 946	if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) {
 947		if (debug)
 948		    printf("Packet received with mode %d\n",
 949			   PKT_MODE(rpkt.li_vn_mode));
 950		goto again;
 951	}
 952	if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) {
 953		if (debug)
 954		    printf("Received request packet, wanted response\n");
 955		goto again;
 956	}
 957
 958	/*
 959	 * Check opcode and sequence number for a match.
 960	 * Could be old data getting to us.
 961	 */
 962	if (ntohs(rpkt.sequence) != sequence) {
 963		if (debug)
 964		    printf(
 965			    "Received sequnce number %d, wanted %d\n",
 966			    ntohs(rpkt.sequence), sequence);
 967		goto again;
 968	}
 969	if (CTL_OP(rpkt.r_m_e_op) != opcode) {
 970		if (debug)
 971		    printf(
 972			    "Received opcode %d, wanted %d (sequence number okay)\n",
 973			    CTL_OP(rpkt.r_m_e_op), opcode);
 974		goto again;
 975	}
 976
 977	/*
 978	 * Check the error code.  If non-zero, return it.
 979	 */
 980	if (CTL_ISERROR(rpkt.r_m_e_op)) {
 981		int errcode;
 982
 983		errcode = (ntohs(rpkt.status) >> 8) & 0xff;
 984		if (debug && CTL_ISMORE(rpkt.r_m_e_op)) {
 985			printf("Error code %d received on not-final packet\n",
 986			       errcode);
 987		}
 988		if (errcode == CERR_UNSPEC)
 989		    return ERR_UNSPEC;
 990		return errcode;
 991	}
 992
 993	/*
 994	 * Check the association ID to make sure it matches what
 995	 * we sent.
 996	 */
 997	if (ntohs(rpkt.associd) != associd) {
 998		if (debug)
 999		    printf("Association ID %d doesn't match expected %d\n",
1000			   ntohs(rpkt.associd), associd);
1001		/*
1002		 * Hack for silly fuzzballs which, at the time of writing,
1003		 * return an assID of sys.peer when queried for system variables.
1004		 */
1005#ifdef notdef
1006		goto again;
1007#endif
1008	}
1009
1010	/*
1011	 * Collect offset and count.  Make sure they make sense.
1012	 */
1013	offset = ntohs(rpkt.offset);
1014	count = ntohs(rpkt.count);
1015
1016	if (debug >= 3) {
1017		int shouldbesize;
1018		u_long key;
1019		u_long *lpkt;
1020		int maclen;
1021
1022		/*
1023		 * Usually we ignore authentication, but for debugging purposes
1024		 * we watch it here.
1025		 */
1026		shouldbesize = CTL_HEADER_LEN + count;
1027
1028		/* round to 8 octet boundary */
1029		shouldbesize = (shouldbesize + 7) & ~7;
1030
1031		if (n & 0x3) {
1032			printf("Packet not padded, size = %d\n", n);
1033		} if ((maclen = n - shouldbesize) >= MIN_MAC_LEN) {
1034			printf(
1035				"Packet shows signs of authentication (total %d, data %d, mac %d)\n",
1036				n, shouldbesize, maclen);
1037			lpkt = (u_long *)&rpkt;
1038			printf("%08lx %08lx %08lx %08lx %08lx %08lx\n",
1039			       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 3]),
1040			       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 2]),
1041			       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 1]),
1042			       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long)]),
1043			       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) + 1]),
1044			       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) + 2]));
1045			key = ntohl(lpkt[(n - maclen) / sizeof(u_long)]);
1046			printf("Authenticated with keyid %lu\n", (u_long)key);
1047			if (key != 0 && key != info_auth_keyid) {
1048				printf("We don't know that key\n");
1049			} else {
1050				if (authdecrypt(key, (u_int32 *)&rpkt,
1051				    n - maclen, maclen)) {
1052					printf("Auth okay!\n");
1053				} else {
1054					printf("Auth failed!\n");
1055				}
1056			}
1057		}
1058	}
1059
1060	if (debug >= 2)
1061	    printf("Got packet, size = %d\n", n);
1062	if (count > (u_short)(n-CTL_HEADER_LEN)) {
1063		if (debug)
1064		    printf(
1065			    "Received count of %d octets, data in packet is %d\n",
1066			    count, n-CTL_HEADER_LEN);
1067		goto again;
1068	}
1069	if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) {
1070		if (debug)
1071		    printf("Received count of 0 in non-final fragment\n");
1072		goto again;
1073	}
1074	if (offset + count > sizeof(pktdata)) {
1075		if (debug)
1076		    printf("Offset %d, count %d, too big for buffer\n",
1077			   offset, count);
1078		return ERR_TOOMUCH;
1079	}
1080	if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) {
1081		if (debug)
1082		    printf("Received second last fragment packet\n");
1083		goto again;
1084	}
1085
1086	/*
1087	 * So far, so good.  Record this fragment, making sure it doesn't
1088	 * overlap anything.
1089	 */
1090	if (debug >= 2)
1091	    printf("Packet okay\n");;
1092
1093	if (numfrags == MAXFRAGS) {
1094		if (debug)
1095		    printf("Number of fragments exceeds maximum\n");
1096		return ERR_TOOMUCH;
1097	}
1098	
1099	for (n = 0; n < numfrags; n++) {
1100		if (offset == offsets[n])
1101		    goto again;	/* duplicate */
1102		if (offset < offsets[n])
1103		    break;
1104	}
1105	
1106	if ((u_short)(n > 0 && offsets[n-1] + counts[n-1]) > offset)
1107	    goto overlap;
1108	if (n < numfrags && (u_short)(offset + count) > offsets[n])
1109	    goto overlap;
1110	
1111	{
1112		register int i;
1113		
1114		for (i = numfrags; i > n; i--) {
1115			offsets[i] = offsets[i-1];
1116			counts[i] = counts[i-1];
1117		}
1118	}
1119	offsets[n] = offset;
1120	counts[n] = count;
1121	numfrags++;
1122
1123	/*
1124	 * Got that stuffed in right.  Figure out if this was the last.
1125	 * Record status info out of the last packet.
1126	 */
1127	if (!CTL_ISMORE(rpkt.r_m_e_op)) {
1128		seenlastfrag = 1;
1129		if (rstatus != 0)
1130		    *rstatus = ntohs(rpkt.status);
1131	}
1132
1133	/*
1134	 * Copy the data into the data buffer.
1135	 */
1136	memmove((char *)pktdata + offset, (char *)rpkt.data, count);
1137
1138	/*
1139	 * If we've seen the last fragment, look for holes in the sequence.
1140	 * If there aren't any, we're done.
1141	 */
1142	if (seenlastfrag && offsets[0] == 0) {
1143		for (n = 1; n < numfrags; n++) {
1144			if (offsets[n-1] + counts[n-1] != offsets[n])
1145			    break;
1146		}
1147		if (n == numfrags) {
1148			*rsize = offsets[numfrags-1] + counts[numfrags-1];
1149			return 0;
1150		}
1151	}
1152	goto again;
1153
1154    overlap:
1155	/*
1156	 * Print debugging message about overlapping fragments
1157	 */
1158	if (debug)
1159	    printf("Overlapping fragments returned in response\n");
1160	goto again;
1161}
1162
1163
1164/*
1165 * sendrequest - format and send a request packet
1166 */
1167static int
1168sendrequest(
1169	int opcode,
1170	int associd,
1171	int auth,
1172	int qsize,
1173	char *qdata
1174	)
1175{
1176	struct ntp_control qpkt;
1177	int pktsize;
1178
1179	/*
1180	 * Check to make sure the data will fit in one packet
1181	 */
1182	if (qsize > CTL_MAX_DATA_LEN) {
1183		(void) fprintf(stderr,
1184			       "***Internal error!  qsize (%d) too large\n",
1185			       qsize);
1186		return 1;
1187	}
1188
1189	/*
1190	 * Fill in the packet
1191	 */
1192	qpkt.li_vn_mode = PKT_LI_VN_MODE(0, pktversion, MODE_CONTROL);
1193	qpkt.r_m_e_op = (u_char)(opcode & CTL_OP_MASK);
1194	qpkt.sequence = htons(sequence);
1195	qpkt.status = 0;
1196	qpkt.associd = htons((u_short)associd);
1197	qpkt.offset = 0;
1198	qpkt.count = htons((u_short)qsize);
1199
1200	/*
1201	 * If we have data, copy it in and pad it out to a 64
1202	 * bit boundary.
1203	 */
1204	if (qsize > 0) {
1205		memmove((char *)qpkt.data, qdata, (unsigned)qsize);
1206		pktsize = qsize + CTL_HEADER_LEN;
1207		while (pktsize & (sizeof(u_long) - 1)) {
1208			qpkt.data[qsize++] = 0;
1209			pktsize++;
1210		}
1211	} else {
1212		pktsize = CTL_HEADER_LEN;
1213	}
1214
1215	/*
1216	 * If it isn't authenticated we can just send it.  Otherwise
1217	 * we're going to have to think about it a little.
1218	 */
1219	if (!auth && !always_auth) {
1220		return sendpkt((char *)&qpkt, pktsize);
1221	} else {
1222		const char *pass = "\0";
1223		int maclen = 0;
1224		u_long my_keyid;
1225
1226		/*
1227		 * Pad out packet to a multiple of 8 octets to be sure
1228		 * receiver can handle it.
1229		 */
1230		while (pktsize & 7) {
1231			qpkt.data[qsize++] = 0;
1232			pktsize++;
1233		}
1234
1235		/*
1236		 * Get the keyid and the password if we don't have one.
1237		 */
1238		if (info_auth_keyid == 0) {
1239			int u_keyid = getkeyid("Keyid: ");
1240			if (u_keyid == 0 || u_keyid > NTP_MAXKEY) {
1241				(void) fprintf(stderr,
1242				   "Invalid key identifier\n");
1243				return 1;
1244			}
1245			info_auth_keyid = u_keyid;
1246		}
1247		if (!authistrusted(info_auth_keyid)) {
1248			pass = getpass("MD5 Password: ");
1249			if (*pass == '\0') {
1250				(void) fprintf(stderr,
1251				  "Invalid password\n");
1252				return (1);
1253			}
1254		}
1255		authusekey(info_auth_keyid, info_auth_keytype, (const u_char *)pass);
1256		authtrust(info_auth_keyid, 1);
1257
1258		/*
1259		 * Stick the keyid in the packet where
1260		 * cp currently points.  Cp should be aligned
1261		 * properly.  Then do the encryptions.
1262		 */
1263		my_keyid = htonl(info_auth_keyid);
1264		memcpy(&qpkt.data[qsize], &my_keyid, sizeof my_keyid);
1265		maclen = authencrypt(info_auth_keyid, (u_int32 *)&qpkt,
1266		    pktsize);
1267		if (maclen == 0) {
1268			(void) fprintf(stderr, "Key not found\n");
1269			return (1);
1270		}
1271		return sendpkt((char *)&qpkt, pktsize + maclen);
1272	}
1273	/*NOTREACHED*/
1274}
1275
1276
1277/*
1278 * doquery - send a request and process the response
1279 */
1280int
1281doquery(
1282	int opcode,
1283	int associd,
1284	int auth,
1285	int qsize,
1286	char *qdata,
1287	u_short *rstatus,
1288	int *rsize,
1289	char **rdata
1290	)
1291{
1292	int res;
1293	int done;
1294
1295	/*
1296	 * Check to make sure host is open
1297	 */
1298	if (!havehost) {
1299		(void) fprintf(stderr, "***No host open, use `host' command\n");
1300		return -1;
1301	}
1302
1303	done = 0;
1304	sequence++;
1305
1306    again:
1307	/*
1308	 * send a request
1309	 */
1310	res = sendrequest(opcode, associd, auth, qsize, qdata);
1311	if (res != 0)
1312	    return res;
1313	
1314	/*
1315	 * Get the response.  If we got a standard error, print a message
1316	 */
1317	res = getresponse(opcode, associd, rstatus, rsize, rdata, done);
1318
1319	if (res > 0) {
1320		if (!done && (res == ERR_TIMEOUT || res == ERR_INCOMPLETE)) {
1321			if (res == ERR_INCOMPLETE) {
1322				/*
1323				 * better bump the sequence so we don't
1324				 * get confused about differing fragments.
1325				 */
1326				sequence++;
1327			}
1328			done = 1;
1329			goto again;
1330		}
1331		if (numhosts > 1)
1332			(void) fprintf(stderr, "server=%s ", currenthost);
1333		switch(res) {
1334		    case CERR_BADFMT:
1335			(void) fprintf(stderr,
1336			    "***Server reports a bad format request packet\n");
1337			break;
1338		    case CERR_PERMISSION:
1339			(void) fprintf(stderr,
1340			    "***Server disallowed request (authentication?)\n");
1341			break;
1342		    case CERR_BADOP:
1343			(void) fprintf(stderr,
1344			    "***Server reports a bad opcode in request\n");
1345			break;
1346		    case CERR_BADASSOC:
1347			(void) fprintf(stderr,
1348			    "***Association ID %d unknown to server\n",associd);
1349			break;
1350		    case CERR_UNKNOWNVAR:
1351			(void) fprintf(stderr,
1352			    "***A request variable unknown to the server\n");
1353			break;
1354		    case CERR_BADVALUE:
1355			(void) fprintf(stderr,
1356			    "***Server indicates a request variable was bad\n");
1357			break;
1358		    case ERR_UNSPEC:
1359			(void) fprintf(stderr,
1360			    "***Server returned an unspecified error\n");
1361			break;
1362		    case ERR_TIMEOUT:
1363			(void) fprintf(stderr, "***Request timed out\n");
1364			break;
1365		    case ERR_INCOMPLETE:
1366			(void) fprintf(stderr,
1367			    "***Response from server was incomplete\n");
1368			break;
1369		    case ERR_TOOMUCH:
1370			(void) fprintf(stderr,
1371			    "***Buffer size exceeded for returned data\n");
1372			break;
1373		    default:
1374			(void) fprintf(stderr,
1375			    "***Server returns unknown error code %d\n", res);
1376			break;
1377		}
1378	}
1379	return res;
1380}
1381
1382
1383/*
1384 * getcmds - read commands from the standard input and execute them
1385 */
1386static void
1387getcmds(void)
1388{
1389#if defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDIT)
1390        char *line;
1391
1392        for (;;) {
1393                if ((line = readline(interactive?prompt:"")) == NULL) return;
1394                if (*line) add_history(line);
1395                docmd(line);
1396                free(line);
1397        }
1398#else /* not (HAVE_LIBREADLINE || HAVE_LIBEDIT) */
1399        char line[MAXLINE];
1400
1401        for (;;) {
1402                if (interactive) {
1403#ifdef VMS      /* work around a problem with mixing stdout & stderr */
1404                        fputs("",stdout);
1405#endif
1406                        (void) fputs(prompt, stderr);
1407                        (void) fflush(stderr);
1408                }
1409
1410                if (fgets(line, sizeof line, stdin) == NULL)
1411                    return;
1412
1413                docmd(line);
1414        }
1415#endif /* not (HAVE_LIBREADLINE || HAVE_LIBEDIT) */
1416}
1417
1418#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
1419/*
1420 * abortcmd - catch interrupts and abort the current command
1421 */
1422static RETSIGTYPE
1423abortcmd(
1424	int sig
1425	)
1426{
1427	if (current_output == stdout)
1428	    (void) fflush(stdout);
1429	putc('\n', stderr);
1430	(void) fflush(stderr);
1431	if (jump) longjmp(interrupt_buf, 1);
1432}
1433#endif	/* SYS_WINNT */
1434
1435/*
1436 * docmd - decode the command line and execute a command
1437 */
1438static void
1439docmd(
1440	const char *cmdline
1441	)
1442{
1443	char *tokens[1+MAXARGS+2];
1444	struct parse pcmd;
1445	int ntok;
1446	static int i;
1447	struct xcmd *xcmd;
1448
1449	/*
1450	 * Tokenize the command line.  If nothing on it, return.
1451	 */
1452	tokenize(cmdline, tokens, &ntok);
1453	if (ntok == 0)
1454	    return;
1455	
1456	/*
1457	 * Find the appropriate command description.
1458	 */
1459	i = findcmd(tokens[0], builtins, opcmds, &xcmd);
1460	if (i == 0) {
1461		(void) fprintf(stderr, "***Command `%s' unknown\n",
1462			       tokens[0]);
1463		return;
1464	} else if (i >= 2) {
1465		(void) fprintf(stderr, "***Command `%s' ambiguous\n",
1466			       tokens[0]);
1467		return;
1468	}
1469	
1470	/*
1471	 * Save the keyword, then walk through the arguments, interpreting
1472	 * as we go.
1473	 */
1474	pcmd.keyword = tokens[0];
1475	pcmd.nargs = 0;
1476	for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) {
1477		if ((i+1) >= ntok) {
1478			if (!(xcmd->arg[i] & OPT)) {
1479				printusage(xcmd, stderr);
1480				return;
1481			}
1482			break;
1483		}
1484		if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>'))
1485		    break;
1486		if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i]))
1487		    return;
1488		pcmd.nargs++;
1489	}
1490
1491	i++;
1492	if (i < ntok && *tokens[i] == '>') {
1493		char *fname;
1494
1495		if (*(tokens[i]+1) != '\0')
1496		    fname = tokens[i]+1;
1497		else if ((i+1) < ntok)
1498		    fname = tokens[i+1];
1499		else {
1500			(void) fprintf(stderr, "***No file for redirect\n");
1501			return;
1502		}
1503
1504		current_output = fopen(fname, "w");
1505		if (current_output == NULL) {
1506			(void) fprintf(stderr, "***Error opening %s: ", fname);
1507			perror("");
1508			return;
1509		}
1510		i = 1;		/* flag we need a close */
1511	} else {
1512		current_output = stdout;
1513		i = 0;		/* flag no close */
1514	}
1515
1516	if (interactive && setjmp(interrupt_buf)) {
1517		jump = 0;
1518		return;
1519	} else {
1520		jump++;
1521		(xcmd->handler)(&pcmd, current_output);
1522		jump = 0;	/* HMS: 961106: was after fclose() */
1523		if (i) (void) fclose(current_output);
1524	}
1525}
1526
1527
1528/*
1529 * tokenize - turn a command line into tokens
1530 */
1531static void
1532tokenize(
1533	const char *line,
1534	char **tokens,
1535	int *ntok
1536	)
1537{
1538	register const char *cp;
1539	register char *sp;
1540	static char tspace[MAXLINE];
1541
1542	sp = tspace;
1543	cp = line;
1544	for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
1545		tokens[*ntok] = sp;
1546		while (ISSPACE(*cp))
1547		    cp++;
1548		if (ISEOL(*cp))
1549		    break;
1550		do {
1551			*sp++ = *cp++;
1552		} while (!ISSPACE(*cp) && !ISEOL(*cp));
1553
1554		*sp++ = '\0';
1555	}
1556}
1557
1558
1559
1560/*
1561 * findcmd - find a command in a command description table
1562 */
1563static int
1564findcmd(
1565	register char *str,
1566	struct xcmd *clist1,
1567	struct xcmd *clist2,
1568	struct xcmd **cmd
1569	)
1570{
1571	register struct xcmd *cl;
1572	register int clen;
1573	int nmatch;
1574	struct xcmd *nearmatch = NULL;
1575	struct xcmd *clist;
1576
1577	clen = strlen(str);
1578	nmatch = 0;
1579	if (clist1 != 0)
1580	    clist = clist1;
1581	else if (clist2 != 0)
1582	    clist = clist2;
1583	else
1584	    return 0;
1585
1586    again:
1587	for (cl = clist; cl->keyword != 0; cl++) {
1588		/* do a first character check, for efficiency */
1589		if (*str != *(cl->keyword))
1590		    continue;
1591		if (strncmp(str, cl->keyword, (unsigned)clen) == 0) {
1592			/*
1593			 * Could be extact match, could be approximate.
1594			 * Is exact if the length of the keyword is the
1595			 * same as the str.
1596			 */
1597			if (*((cl->keyword) + clen) == '\0') {
1598				*cmd = cl;
1599				return 1;
1600			}
1601			nmatch++;
1602			nearmatch = cl;
1603		}
1604	}
1605
1606	/*
1607	 * See if there is more to do.  If so, go again.  Sorry about the
1608	 * goto, too much looking at BSD sources...
1609	 */
1610	if (clist == clist1 && clist2 != 0) {
1611		clist = clist2;
1612		goto again;
1613	}
1614
1615	/*
1616	 * If we got extactly 1 near match, use it, else return number
1617	 * of matches.
1618	 */
1619	if (nmatch == 1) {
1620		*cmd = nearmatch;
1621		return 1;
1622	}
1623	return nmatch;
1624}
1625
1626
1627/*
1628 * getarg - interpret an argument token
1629 */
1630static int
1631getarg(
1632	char *str,
1633	int code,
1634	arg_v *argp
1635	)
1636{
1637	int isneg;
1638	char *cp, *np;
1639	static const char *digits = "0123456789";
1640
1641	switch (code & ~OPT) {
1642	    case NTP_STR:
1643		argp->string = str;
1644		break;
1645	    case NTP_ADD:
1646		if (!getnetnum(str, &(argp->netnum), (char *)0, 0)) {
1647			return 0;
1648		}
1649		break;
1650	    case NTP_INT:
1651	    case NTP_UINT:
1652		isneg = 0;
1653		np = str;
1654		if (*np == '&') {
1655			np++;
1656			isneg = atoi(np);
1657			if (isneg <= 0) {
1658				(void) fprintf(stderr,
1659					       "***Association value `%s' invalid/undecodable\n", str);
1660				return 0;
1661			}
1662			if (isneg > numassoc) {
1663				if (numassoc == 0) {
1664					(void) fprintf(stderr,
1665						       "***Association for `%s' unknown (max &%d)\n",
1666						       str, numassoc);
1667					return 0;
1668				} else {
1669					isneg = numassoc;
1670				}
1671			}
1672			argp->uval = assoc_cache[isneg-1].assid;
1673			break;
1674		}
1675
1676		if (*np == '-') {
1677			np++;
1678			isneg = 1;
1679		}
1680
1681		argp->uval = 0;
1682		do {
1683			cp = strchr(digits, *np);
1684			if (cp == NULL) {
1685				(void) fprintf(stderr,
1686					       "***Illegal integer value %s\n", str);
1687				return 0;
1688			}
1689			argp->uval *= 10;
1690			argp->uval += (cp - digits);
1691		} while (*(++np) != '\0');
1692
1693		if (isneg) {
1694			if ((code & ~OPT) == NTP_UINT) {
1695				(void) fprintf(stderr,
1696					       "***Value %s should be unsigned\n", str);
1697				return 0;
1698			}
1699			argp->ival = -argp->ival;
1700		}
1701		break;
1702	     case IP_VERSION:
1703		if (!strcmp("-6", str))
1704			argp->ival = 6 ;
1705		else if (!strcmp("-4", str))
1706			argp->ival = 4 ;
1707		else {
1708			(void) fprintf(stderr,
1709			    "***Version must be either 4 or 6\n");
1710			return 0;
1711		}
1712		break;
1713	}
1714
1715	return 1;
1716}
1717
1718
1719/*
1720 * getnetnum - given a host name, return its net number
1721 *	       and (optional) full name
1722 */
1723int
1724getnetnum(
1725	const char *hname,
1726	struct sockaddr_storage *num,
1727	char *fullhost,
1728	int af
1729	)
1730{
1731	int sockaddr_len;
1732	struct addrinfo hints, *ai = NULL;
1733
1734	sockaddr_len = (af == AF_INET)
1735			   ? sizeof(struct sockaddr_in)
1736			   : sizeof(struct sockaddr_in6);
1737	memset((char *)&hints, 0, sizeof(struct addrinfo));
1738	hints.ai_flags = AI_CANONNAME;
1739#ifdef AI_ADDRCONFIG
1740	hints.ai_flags |= AI_ADDRCONFIG;
1741#endif
1742	
1743	/* decodenetnum works with addresses only */
1744	if (decodenetnum(hname, num)) {
1745		if (fullhost != 0) {
1746			getnameinfo((struct sockaddr *)num, sockaddr_len,
1747					fullhost, sizeof(fullhost), NULL, 0,
1748					NI_NUMERICHOST);
1749		}
1750		return 1;
1751	} else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) {
1752		memmove((char *)num, ai->ai_addr, ai->ai_addrlen);
1753		if (ai->ai_canonname != 0)
1754		    (void) strcpy(fullhost, ai->ai_canonname);
1755		return 1;
1756	} else {
1757		(void) fprintf(stderr, "***Can't find host %s\n", hname);
1758		return 0;
1759	}
1760	/*NOTREACHED*/
1761}
1762
1763/*
1764 * nntohost - convert network number to host name.  This routine enforces
1765 *	       the showhostnames setting.
1766 */
1767char *
1768nntohost(
1769	struct sockaddr_storage *netnum
1770	)
1771{
1772	if (!showhostnames)
1773	    return stoa(netnum);
1774	if ((netnum->ss_family == AF_INET) && ISREFCLOCKADR(netnum))
1775    		return refnumtoa(netnum);
1776	return socktohost(netnum);
1777}
1778
1779
1780/*
1781 * rtdatetolfp - decode an RT-11 date into an l_fp
1782 */
1783static int
1784rtdatetolfp(
1785	char *str,
1786	l_fp *lfp
1787	)
1788{
1789	register char *cp;
1790	register int i;
1791	struct calendar cal;
1792	char buf[4];
1793	static const char *months[12] = {
1794		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
1795		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1796	};
1797
1798	cal.yearday = 0;
1799
1800	/*
1801	 * An RT-11 date looks like:
1802	 *
1803	 * d[d]-Mth-y[y] hh:mm:ss
1804	 *
1805	 * (No docs, but assume 4-digit years are also legal...)
1806	 *
1807	 * d[d]-Mth-y[y[y[y]]] hh:mm:ss
1808	 */
1809	cp = str;
1810	if (!isdigit((int)*cp)) {
1811		if (*cp == '-') {
1812			/*
1813			 * Catch special case
1814			 */
1815			L_CLR(lfp);
1816			return 1;
1817		}
1818		return 0;
1819	}
1820
1821	cal.monthday = (u_char) (*cp++ - '0');	/* ascii dependent */
1822	if (isdigit((int)*cp)) {
1823		cal.monthday = (u_char)((cal.monthday << 3) + (cal.monthday << 1));
1824		cal.monthday = (u_char)(cal.monthday + *cp++ - '0');
1825	}
1826
1827	if (*cp++ != '-')
1828	    return 0;
1829	
1830	for (i = 0; i < 3; i++)
1831	    buf[i] = *cp++;
1832	buf[3] = '\0';
1833
1834	for (i = 0; i < 12; i++)
1835	    if (STREQ(buf, months[i]))
1836		break;
1837	if (i == 12)
1838	    return 0;
1839	cal.month = (u_char)(i + 1);
1840
1841	if (*cp++ != '-')
1842	    return 0;
1843	
1844	if (!isdigit((int)*cp))
1845	    return 0;
1846	cal.year = (u_short)(*cp++ - '0');
1847	if (isdigit((int)*cp)) {
1848		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
1849		cal.year = (u_short)(*cp++ - '0');
1850	}
1851	if (isdigit((int)*cp)) {
1852		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
1853		cal.year = (u_short)(cal.year + *cp++ - '0');
1854	}
1855	if (isdigit((int)*cp)) {
1856		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
1857		cal.year = (u_short)(cal.year + *cp++ - '0');
1858	}
1859
1860	/*
1861	 * Catch special case.  If cal.year == 0 this is a zero timestamp.
1862	 */
1863	if (cal.year == 0) {
1864		L_CLR(lfp);
1865		return 1;
1866	}
1867
1868	if (*cp++ != ' ' || !isdigit((int)*cp))
1869	    return 0;
1870	cal.hour = (u_char)(*cp++ - '0');
1871	if (isdigit((int)*cp)) {
1872		cal.hour = (u_char)((cal.hour << 3) + (cal.hour << 1));
1873		cal.hour = (u_char)(cal.hour + *cp++ - '0');
1874	}
1875
1876	if (*cp++ != ':' || !isdigit((int)*cp))
1877	    return 0;
1878	cal.minute = (u_char)(*cp++ - '0');
1879	if (isdigit((int)*cp)) {
1880		cal.minute = (u_char)((cal.minute << 3) + (cal.minute << 1));
1881		cal.minute = (u_char)(cal.minute + *cp++ - '0');
1882	}
1883
1884	if (*cp++ != ':' || !isdigit((int)*cp))
1885	    return 0;
1886	cal.second = (u_char)(*cp++ - '0');
1887	if (isdigit((int)*cp)) {
1888		cal.second = (u_char)((cal.second << 3) + (cal.second << 1));
1889		cal.second = (u_char)(cal.second + *cp++ - '0');
1890	}
1891
1892	/*
1893	 * For RT-11, 1972 seems to be the pivot year
1894	 */
1895	if (cal.year < 72)
1896		cal.year += 2000;
1897	if (cal.year < 100)
1898		cal.year += 1900;
1899
1900	lfp->l_ui = caltontp(&cal);
1901	lfp->l_uf = 0;
1902	return 1;
1903}
1904
1905
1906/*
1907 * decodets - decode a timestamp into an l_fp format number, with
1908 *	      consideration of fuzzball formats.
1909 */
1910int
1911decodets(
1912	char *str,
1913	l_fp *lfp
1914	)
1915{
1916	/*
1917	 * If it starts with a 0x, decode as hex.
1918	 */
1919	if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
1920	    return hextolfp(str+2, lfp);
1921
1922	/*
1923	 * If it starts with a '"', try it as an RT-11 date.
1924	 */
1925	if (*str == '"') {
1926		register char *cp = str+1;
1927		register char *bp;
1928		char buf[30];
1929
1930		bp = buf;
1931		while (*cp != '"' && *cp != '\0' && bp < &buf[29])
1932		    *bp++ = *cp++;
1933		*bp = '\0';
1934		return rtdatetolfp(buf, lfp);
1935	}
1936
1937	/*
1938	 * Might still be hex.  Check out the first character.  Talk
1939	 * about heuristics!
1940	 */
1941	if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f'))
1942	    return hextolfp(str, lfp);
1943
1944	/*
1945	 * Try it as a decimal.  If this fails, try as an unquoted
1946	 * RT-11 date.  This code should go away eventually.
1947	 */
1948	if (atolfp(str, lfp))
1949	    return 1;
1950	return rtdatetolfp(str, lfp);
1951}
1952
1953
1954/*
1955 * decodetime - decode a time value.  It should be in milliseconds
1956 */
1957int
1958decodetime(
1959	char *str,
1960	l_fp *lfp
1961	)
1962{
1963	return mstolfp(str, lfp);
1964}
1965
1966
1967/*
1968 * decodeint - decode an integer
1969 */
1970int
1971decodeint(
1972	char *str,
1973	long *val
1974	)
1975{
1976	if (*str == '0') {
1977		if (*(str+1) == 'x' || *(str+1) == 'X')
1978		    return hextoint(str+2, val);
1979		return octtoint(str, val);
1980	}
1981	return atoint(str, val);
1982}
1983
1984
1985/*
1986 * decodeuint - decode an unsigned integer
1987 */
1988int
1989decodeuint(
1990	char *str,
1991	u_long *val
1992	)
1993{
1994	if (*str == '0') {
1995		if (*(str + 1) == 'x' || *(str + 1) == 'X')
1996			return (hextoint(str + 2, val));
1997		return (octtoint(str, val));
1998	}
1999	return (atouint(str, val));
2000}
2001
2002
2003/*
2004 * decodearr - decode an array of time values
2005 */
2006static int
2007decodearr(
2008	char *str,
2009	int *narr,
2010	l_fp *lfparr
2011	)
2012{
2013	register char *cp, *bp;
2014	register l_fp *lfp;
2015	char buf[60];
2016
2017	lfp = lfparr;
2018	cp = str;
2019	*narr = 0;
2020
2021	while (*narr < 8) {
2022		while (isspace((int)*cp))
2023		    cp++;
2024		if (*cp == '\0')
2025		    break;
2026
2027		bp = buf;
2028		while (!isspace((int)*cp) && *cp != '\0')
2029		    *bp++ = *cp++;
2030		*bp++ = '\0';
2031
2032		if (!decodetime(buf, lfp))
2033		    return 0;
2034		(*narr)++;
2035		lfp++;
2036	}
2037	return 1;
2038}
2039
2040
2041/*
2042 * Finally, the built in command handlers
2043 */
2044
2045/*
2046 * help - tell about commands, or details of a particular command
2047 */
2048static void
2049help(
2050	struct parse *pcmd,
2051	FILE *fp
2052	)
2053{
2054	struct xcmd *xcp;
2055	char *cmd;
2056	const char *list[100];
2057        int word, words;
2058        int row, rows;
2059        int col, cols;
2060
2061	if (pcmd->nargs == 0) {
2062		words = 0;
2063		for (xcp = builtins; xcp->keyword != 0; xcp++) {
2064			if (*(xcp->keyword) != '?')
2065			    list[words++] = xcp->keyword;
2066		}
2067		for (xcp = opcmds; xcp->keyword != 0; xcp++)
2068		    list[words++] = xcp->keyword;
2069
2070		qsort(
2071#ifdef QSORT_USES_VOID_P
2072		    (void *)
2073#else
2074		    (char *)
2075#endif
2076			(list), (size_t)(words), sizeof(char *), helpsort);
2077		col = 0;
2078		for (word = 0; word < words; word++) {
2079		 	int length = strlen(list[word]);
2080			if (col < length) {
2081			    col = length;
2082                        }
2083		}
2084
2085		cols = SCREENWIDTH / ++col;
2086                rows = (words + cols - 1) / cols;
2087
2088		(void) fprintf(fp, "ntpq commands:\n");
2089
2090		for (row = 0; row < rows; row++) {
2091                        for (word = row; word < words; word += rows) {
2092			        (void) fprintf(fp, "%-*.*s", col, col-1, list[word]);
2093                        }
2094                        (void) fprintf(fp, "\n");
2095                }
2096	} else {
2097		cmd = pcmd->argval[0].string;
2098		words = findcmd(cmd, builtins, opcmds, &xcp);
2099		if (words == 0) {
2100			(void) fprintf(stderr,
2101				       "Command `%s' is unknown\n", cmd);
2102			return;
2103		} else if (words >= 2) {
2104			(void) fprintf(stderr,
2105				       "Command `%s' is ambiguous\n", cmd);
2106			return;
2107		}
2108		(void) fprintf(fp, "function: %s\n", xcp->comment);
2109		printusage(xcp, fp);
2110	}
2111}
2112
2113
2114/*
2115 * helpsort - do hostname qsort comparisons
2116 */
2117#ifdef QSORT_USES_VOID_P
2118static int
2119helpsort(
2120	const void *t1,
2121	const void *t2
2122	)
2123{
2124	char const * const * name1 = (char const * const *)t1;
2125	char const * const * name2 = (char const * const *)t2;
2126
2127	return strcmp(*name1, *name2);
2128}
2129
2130#else
2131static int
2132helpsort(
2133	char **name1,
2134	char **name2
2135	)
2136{
2137	return strcmp(*name1, *name2);
2138}
2139#endif
2140
2141/*
2142 * printusage - print usage information for a command
2143 */
2144static void
2145printusage(
2146	struct xcmd *xcp,
2147	FILE *fp
2148	)
2149{
2150	register int i;
2151
2152	(void) fprintf(fp, "usage: %s", xcp->keyword);
2153	for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
2154		if (xcp->arg[i] & OPT)
2155		    (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
2156		else
2157		    (void) fprintf(fp, " %s", xcp->desc[i]);
2158	}
2159	(void) fprintf(fp, "\n");
2160}
2161
2162
2163/*
2164 * timeout - set time out time
2165 */
2166static void
2167timeout(
2168	struct parse *pcmd,
2169	FILE *fp
2170	)
2171{
2172	int val;
2173
2174	if (pcmd->nargs == 0) {
2175		val = tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
2176		(void) fprintf(fp, "primary timeout %d ms\n", val);
2177	} else {
2178		tvout.tv_sec = pcmd->argval[0].uval / 1000;
2179		tvout.tv_usec = (pcmd->argval[0].uval - (tvout.tv_sec * 1000))
2180			* 1000;
2181	}
2182}
2183
2184
2185/*
2186 * auth_delay - set delay for auth requests
2187 */
2188static void
2189auth_delay(
2190	struct parse *pcmd,
2191	FILE *fp
2192	)
2193{
2194	int isneg;
2195	u_long val;
2196
2197	if (pcmd->nargs == 0) {
2198		val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
2199		(void) fprintf(fp, "delay %lu ms\n", val);
2200	} else {
2201		if (pcmd->argval[0].ival < 0) {
2202			isneg = 1;
2203			val = (u_long)(-pcmd->argval[0].ival);
2204		} else {
2205			isneg = 0;
2206			val = (u_long)pcmd->argval[0].ival;
2207

Large files files are truncated, but you can click here to view the full file