PageRenderTime 132ms CodeModel.GetById 42ms app.highlight 76ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/ntp/ntpdc/ntpdc.c

https://bitbucket.org/freebsd/freebsd-head/
C | 1999 lines | 1502 code | 221 blank | 276 comment | 400 complexity | 794df605728909a9e9a16fe741cee32a MD5 | raw file
   1/*
   2 * ntpdc - control and monitor your ntpd daemon
   3 */
   4
   5#include <stdio.h>
   6
   7#include <ctype.h>
   8#include <signal.h>
   9#include <setjmp.h>
  10
  11#include "ntpdc.h"
  12#include "ntp_select.h"
  13#include "ntp_io.h"
  14#include "ntp_stdlib.h"
  15/* Don't include ISC's version of IPv6 variables and structures */
  16#define ISC_IPV6_H 1
  17#include "isc/net.h"
  18#include "isc/result.h"
  19
  20#include "ntpdc-opts.h"
  21
  22#ifdef SYS_WINNT
  23# include <Mswsock.h>
  24# include <io.h>
  25#else
  26# define closesocket close
  27#endif /* SYS_WINNT */
  28
  29#if defined(HAVE_LIBREADLINE) || defined (HAVE_LIBEDIT)
  30# include <readline/readline.h>
  31# include <readline/history.h>
  32#endif /* HAVE_LIBREADLINE || HAVE_LIBEDIT */
  33
  34#ifdef SYS_VXWORKS
  35				/* vxWorks needs mode flag -casey*/
  36# define open(name, flags)   open(name, flags, 0777)
  37# define SERVER_PORT_NUM     123
  38#endif
  39
  40/* We use COMMAND as an autogen keyword */
  41#ifdef COMMAND
  42# undef COMMAND
  43#endif
  44
  45/*
  46 * Because we now potentially understand a lot of commands (and
  47 * it requires a lot of commands to talk to ntpd) we will run
  48 * interactive if connected to a terminal.
  49 */
  50static	int	interactive = 0;	/* set to 1 when we should prompt */
  51static	const char *	prompt = "ntpdc> ";	/* prompt to ask him about */
  52
  53/*
  54 * Keyid used for authenticated requests.  Obtained on the fly.
  55 */
  56static	u_long	info_auth_keyid;
  57static int keyid_entered = 0;
  58
  59/*
  60 * Type of key md5
  61 */
  62#define	KEY_TYPE_MD5	4
  63
  64static	int info_auth_keytype = KEY_TYPE_MD5;	/* MD5 */
  65u_long	current_time;		/* needed by authkeys; not used */
  66
  67/*
  68 * for get_systime()
  69 */
  70s_char	sys_precision;		/* local clock precision (log2 s) */
  71
  72int		ntpdcmain	P((int,	char **));
  73/*
  74 * Built in command handler declarations
  75 */
  76static	int	openhost	P((const char *));
  77static	int	sendpkt		P((char *, int));
  78static	void	growpktdata	P((void));
  79static	int	getresponse	P((int, int, int *, int *, char **, int));
  80static	int	sendrequest	P((int, int, int, int, int, char *));
  81static	void	getcmds		P((void));
  82static	RETSIGTYPE abortcmd	P((int));
  83static	void	docmd		P((const char *));
  84static	void	tokenize	P((const char *, char **, int *));
  85static	int	findcmd		P((char *, struct xcmd *, struct xcmd *, struct xcmd **));
  86static	int	getarg		P((char *, int, arg_v *));
  87static	int	getnetnum	P((const char *, struct sockaddr_storage *, char *, int));
  88static	void	help		P((struct parse *, FILE *));
  89#ifdef QSORT_USES_VOID_P
  90static	int	helpsort	P((const void *, const void *));
  91#else
  92static	int	helpsort	P((char **, char **));
  93#endif
  94static	void	printusage	P((struct xcmd *, FILE *));
  95static	void	timeout		P((struct parse *, FILE *));
  96static	void	my_delay	P((struct parse *, FILE *));
  97static	void	host		P((struct parse *, FILE *));
  98static	void	keyid		P((struct parse *, FILE *));
  99static	void	keytype		P((struct parse *, FILE *));
 100static	void	passwd		P((struct parse *, FILE *));
 101static	void	hostnames	P((struct parse *, FILE *));
 102static	void	setdebug	P((struct parse *, FILE *));
 103static	void	quit		P((struct parse *, FILE *));
 104static	void	version		P((struct parse *, FILE *));
 105static	void	warning		P((const char *, const char *, const char *));
 106static	void	error		P((const char *, const char *, const char *));
 107static	u_long	getkeyid	P((const char *));
 108
 109
 110
 111/*
 112 * Built-in commands we understand
 113 */
 114static	struct xcmd builtins[] = {
 115	{ "?",		help,		{  OPT|NTP_STR, NO, NO, NO },
 116	  { "command", "", "", "" },
 117	  "tell the use and syntax of commands" },
 118	{ "help",	help,		{  OPT|NTP_STR, NO, NO, NO },
 119	  { "command", "", "", "" },
 120	  "tell the use and syntax of commands" },
 121	{ "timeout",	timeout,	{ OPT|NTP_UINT, NO, NO, NO },
 122	  { "msec", "", "", "" },
 123	  "set the primary receive time out" },
 124	{ "delay",	my_delay,	{ OPT|NTP_INT, NO, NO, NO },
 125	  { "msec", "", "", "" },
 126	  "set the delay added to encryption time stamps" },
 127	{ "host",	host,		{ OPT|NTP_STR, OPT|NTP_STR, NO, NO },
 128	  { "-4|-6", "hostname", "", "" },
 129	  "specify the host whose NTP server we talk to" },
 130	{ "passwd",	passwd,		{ OPT|NTP_STR, NO, NO, NO },
 131	  { "", "", "", "" },
 132	  "specify a password to use for authenticated requests"},
 133	{ "hostnames",	hostnames,	{ OPT|NTP_STR, NO, NO, NO },
 134	  { "yes|no", "", "", "" },
 135	  "specify whether hostnames or net numbers are printed"},
 136	{ "debug",	setdebug,	{ OPT|NTP_STR, NO, NO, NO },
 137	  { "no|more|less", "", "", "" },
 138	  "set/change debugging level" },
 139	{ "quit",	quit,		{ NO, NO, NO, NO },
 140	  { "", "", "", "" },
 141	  "exit ntpdc" },
 142	{ "exit",	quit,		{ NO, NO, NO, NO },
 143	  { "", "", "", "" },
 144	  "exit ntpdc" },
 145	{ "keyid",	keyid,		{ OPT|NTP_UINT, NO, NO, NO },
 146	  { "key#", "", "", "" },
 147	  "set/show keyid to use for authenticated requests" },
 148	{ "keytype",	keytype,	{ OPT|NTP_STR, NO, NO, NO },
 149	  { "(md5|des)", "", "", "" },
 150	  "set/show key authentication type for authenticated requests (des|md5)" },
 151	{ "version",	version,	{ NO, NO, NO, NO },
 152	  { "", "", "", "" },
 153	  "print version number" },
 154	{ 0,		0,		{ NO, NO, NO, NO },
 155	  { "", "", "", "" }, "" }
 156};
 157
 158
 159/*
 160 * Default values we use.
 161 */
 162#define	DEFTIMEOUT	(5)		/* 5 second time out */
 163#define	DEFSTIMEOUT	(2)		/* 2 second time out after first */
 164#define	DEFDELAY	0x51EB852	/* 20 milliseconds, l_fp fraction */
 165#define	DEFHOST		"localhost"	/* default host name */
 166#define	LENHOSTNAME	256		/* host name is 256 characters long */
 167#define	MAXCMDS		100		/* maximum commands on cmd line */
 168#define	MAXHOSTS	200		/* maximum hosts on cmd line */
 169#define	MAXLINE		512		/* maximum line length */
 170#define	MAXTOKENS	(1+1+MAXARGS+MOREARGS+2)	/* maximum number of usable tokens */
 171#define	SCREENWIDTH  	78		/* nominal screen width in columns */
 172
 173/*
 174 * Some variables used and manipulated locally
 175 */
 176static	struct timeval tvout = { DEFTIMEOUT, 0 };	/* time out for reads */
 177static	struct timeval tvsout = { DEFSTIMEOUT, 0 };	/* secondary time out */
 178static	l_fp delay_time;				/* delay time */
 179static	char currenthost[LENHOSTNAME];			/* current host name */
 180int showhostnames = 1;					/* show host names by default */
 181
 182static	int ai_fam_templ;				/* address family */
 183static	int ai_fam_default;				/* default address family */
 184static	SOCKET sockfd;					/* fd socket is opened on */
 185static	int havehost = 0;				/* set to 1 when host open */
 186int s_port = 0;
 187
 188#if defined (SYS_WINNT) || defined (SYS_VXWORKS)
 189char password[9];
 190#endif /* SYS_WINNT || SYS_VXWORKS */
 191
 192#ifdef SYS_WINNT
 193DWORD NumberOfBytesWritten;
 194
 195HANDLE	TimerThreadHandle = NULL;	/* 1998/06/03 - Used in ntplib/machines.c */
 196void timer(void)	{  ; };	/* 1998/06/03 - Used in ntplib/machines.c */
 197
 198#endif /* SYS_WINNT */
 199
 200/*
 201 * Holds data returned from queries.  We allocate INITDATASIZE
 202 * octets to begin with, increasing this as we need to.
 203 */
 204#define	INITDATASIZE	(sizeof(struct resp_pkt) * 16)
 205#define	INCDATASIZE	(sizeof(struct resp_pkt) * 8)
 206
 207static	char *pktdata;
 208static	int pktdatasize;
 209
 210/*
 211 * These are used to help the magic with old and new versions of ntpd.
 212 */
 213int impl_ver = IMPL_XNTPD;
 214static int req_pkt_size = REQ_LEN_NOMAC;
 215
 216/*
 217 * For commands typed on the command line (with the -c option)
 218 */
 219static	int numcmds = 0;
 220static	const char *ccmds[MAXCMDS];
 221#define	ADDCMD(cp)	if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
 222
 223/*
 224 * When multiple hosts are specified.
 225 */
 226static	int numhosts = 0;
 227static	const char *chosts[MAXHOSTS];
 228#define	ADDHOST(cp)	if (numhosts < MAXHOSTS) chosts[numhosts++] = (cp)
 229
 230/*
 231 * Error codes for internal use
 232 */
 233#define	ERR_INCOMPLETE		16
 234#define	ERR_TIMEOUT		17
 235
 236/*
 237 * Macro definitions we use
 238 */
 239#define	ISSPACE(c)	((c) == ' ' || (c) == '\t')
 240#define	ISEOL(c)	((c) == '\n' || (c) == '\r' || (c) == '\0')
 241#define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
 242
 243/*
 244 * For converting time stamps to dates
 245 */
 246#define	JAN_1970	2208988800	/* 1970 - 1900 in seconds */
 247
 248/*
 249 * Jump buffer for longjumping back to the command level
 250 */
 251static	jmp_buf interrupt_buf;
 252static  volatile int jump = 0;
 253
 254/*
 255 * Pointer to current output unit
 256 */
 257static	FILE *current_output;
 258
 259/*
 260 * Command table imported from ntpdc_ops.c
 261 */
 262extern struct xcmd opcmds[];
 263
 264char *progname;
 265volatile int debug;
 266
 267#ifdef NO_MAIN_ALLOWED
 268CALL(ntpdc,"ntpdc",ntpdcmain);
 269#else
 270int
 271main(
 272	int argc,
 273	char *argv[]
 274	)
 275{
 276	return ntpdcmain(argc, argv);
 277}
 278#endif
 279
 280#ifdef SYS_VXWORKS
 281void clear_globals(void)
 282{
 283    showhostnames = 0;              /* show host names by default */
 284    havehost = 0;                   /* set to 1 when host open */
 285    numcmds = 0;
 286    numhosts = 0;
 287}
 288#endif
 289
 290/*
 291 * main - parse arguments and handle options
 292 */
 293int
 294ntpdcmain(
 295	int argc,
 296	char *argv[]
 297	)
 298{
 299	extern int ntp_optind;
 300
 301	delay_time.l_ui = 0;
 302	delay_time.l_uf = DEFDELAY;
 303
 304#ifdef SYS_VXWORKS
 305	clear_globals();
 306	taskPrioritySet(taskIdSelf(), 100 );
 307#endif
 308
 309#ifdef SYS_WINNT
 310	if (!Win32InitSockets())
 311	{
 312		fprintf(stderr, "No useable winsock.dll:");
 313		exit(1);
 314	}
 315#endif /* SYS_WINNT */
 316
 317	/* Check to see if we have IPv6. Otherwise force the -4 flag */
 318	if (isc_net_probeipv6() != ISC_R_SUCCESS) {
 319		ai_fam_default = AF_INET;
 320	}
 321
 322	progname = argv[0];
 323
 324	{
 325		int optct = optionProcess(&ntpdcOptions, argc, argv);
 326		argc -= optct;
 327		argv += optct;
 328	}
 329
 330	switch (WHICH_IDX_IPV4) {
 331	    case INDEX_OPT_IPV4:
 332		ai_fam_templ = AF_INET;
 333		break;
 334	    case INDEX_OPT_IPV6:
 335		ai_fam_templ = AF_INET6;
 336		break;
 337	    default:
 338		ai_fam_templ = ai_fam_default;
 339		break;
 340	}
 341
 342	if (HAVE_OPT(COMMAND)) {
 343		int		cmdct = STACKCT_OPT( COMMAND );
 344		const char**	cmds  = STACKLST_OPT( COMMAND );
 345
 346		while (cmdct-- > 0) {
 347			ADDCMD(*cmds++);
 348		}
 349	}
 350
 351	debug = DESC(DEBUG_LEVEL).optOccCt;
 352
 353	if (HAVE_OPT(INTERACTIVE)) {
 354		interactive = 1;
 355	}
 356
 357	if (HAVE_OPT(NUMERIC)) {
 358		showhostnames = 0;
 359	}
 360
 361	if (HAVE_OPT(LISTPEERS)) {
 362		ADDCMD("listpeers");
 363	}
 364
 365	if (HAVE_OPT(PEERS)) {
 366		ADDCMD("peers");
 367	}
 368
 369	if (HAVE_OPT(SHOWPEERS)) {
 370		ADDCMD("dmpeers");
 371	}
 372
 373	if (ntp_optind == argc) {
 374		ADDHOST(DEFHOST);
 375	} else {
 376		for (; ntp_optind < argc; ntp_optind++)
 377		    ADDHOST(argv[ntp_optind]);
 378	}
 379
 380	if (numcmds == 0 && interactive == 0
 381	    && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
 382		interactive = 1;
 383	}
 384
 385#if 0
 386	ai_fam_templ = ai_fam_default;
 387	while ((c = ntp_getopt(argc, argv, "46c:dilnps")) != EOF)
 388	    switch (c) {
 389		case '4':
 390		    ai_fam_templ = AF_INET;
 391		    break;
 392		case '6':
 393		    ai_fam_templ = AF_INET6;
 394		    break;
 395		case 'c':
 396		    ADDCMD(ntp_optarg);
 397		    break;
 398		case 'd':
 399		    ++debug;
 400		    break;
 401		case 'i':
 402		    interactive = 1;
 403		    break;
 404		case 'l':
 405		    ADDCMD("listpeers");
 406		    break;
 407		case 'n':
 408		    showhostnames = 0;
 409		    break;
 410		case 'p':
 411		    ADDCMD("peers");
 412		    break;
 413		case 's':
 414		    ADDCMD("dmpeers");
 415		    break;
 416		default:
 417		    errflg++;
 418		    break;
 419	    }
 420
 421	if (errflg) {
 422		(void) fprintf(stderr,
 423			       "usage: %s [-46dilnps] [-c cmd] host ...\n",
 424			       progname);
 425		exit(2);
 426	}
 427
 428	if (ntp_optind == argc) {
 429		ADDHOST(DEFHOST);
 430	} else {
 431		for (; ntp_optind < argc; ntp_optind++)
 432		    ADDHOST(argv[ntp_optind]);
 433	}
 434
 435	if (numcmds == 0 && interactive == 0
 436	    && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
 437		interactive = 1;
 438	}
 439#endif
 440
 441#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
 442	if (interactive)
 443	    (void) signal_no_reset(SIGINT, abortcmd);
 444#endif /* SYS_WINNT */
 445
 446	/*
 447	 * Initialize the packet data buffer
 448	 */
 449	pktdata = (char *)malloc(INITDATASIZE);
 450	if (pktdata == NULL) {
 451		(void) fprintf(stderr, "%s: malloc() failed!\n", progname);
 452		exit(1);
 453	}
 454	pktdatasize = INITDATASIZE;
 455
 456	if (numcmds == 0) {
 457		(void) openhost(chosts[0]);
 458		getcmds();
 459	} else {
 460		int ihost;
 461		int icmd;
 462
 463		for (ihost = 0; ihost < numhosts; ihost++) {
 464			if (openhost(chosts[ihost]))
 465			    for (icmd = 0; icmd < numcmds; icmd++) {
 466				    if (numhosts > 1) 
 467					printf ("--- %s ---\n",chosts[ihost]);
 468				    docmd(ccmds[icmd]);
 469			    }
 470		}
 471	}
 472#ifdef SYS_WINNT
 473	WSACleanup();
 474#endif
 475	return(0);
 476} /* main end */
 477
 478
 479/*
 480 * openhost - open a socket to a host
 481 */
 482static int
 483openhost(
 484	const char *hname
 485	)
 486{
 487	char temphost[LENHOSTNAME];
 488	int a_info, i;
 489	struct addrinfo hints, *ai = NULL;
 490	register const char *cp;
 491	char name[LENHOSTNAME];
 492	char service[5];
 493
 494	/*
 495	 * We need to get by the [] if they were entered 
 496	 */
 497	
 498	cp = hname;
 499	
 500	if (*cp == '[') {
 501		cp++;	
 502		for(i = 0; *cp != ']'; cp++, i++)
 503			name[i] = *cp;	
 504		name[i] = '\0';
 505		hname = name;
 506	}	
 507
 508	/*
 509	 * First try to resolve it as an ip address and if that fails,
 510	 * do a fullblown (dns) lookup. That way we only use the dns
 511	 * when it is needed and work around some implementations that
 512	 * will return an "IPv4-mapped IPv6 address" address if you
 513	 * give it an IPv4 address to lookup.
 514	 */
 515	strcpy(service, "ntp");
 516	memset((char *)&hints, 0, sizeof(struct addrinfo));
 517	hints.ai_family = ai_fam_templ;
 518	hints.ai_protocol = IPPROTO_UDP;
 519	hints.ai_socktype = SOCK_DGRAM;
 520	hints.ai_flags = AI_NUMERICHOST;
 521
 522	a_info = getaddrinfo(hname, service, &hints, &ai);
 523	if (a_info == EAI_NONAME
 524#ifdef EAI_NODATA
 525	    || a_info == EAI_NODATA
 526#endif
 527	   ) {
 528		hints.ai_flags = AI_CANONNAME;
 529#ifdef AI_ADDRCONFIG
 530		hints.ai_flags |= AI_ADDRCONFIG;
 531#endif
 532		a_info = getaddrinfo(hname, service, &hints, &ai);	
 533	}
 534	/* Some older implementations don't like AI_ADDRCONFIG. */
 535	if (a_info == EAI_BADFLAGS) {
 536		hints.ai_flags = AI_CANONNAME;
 537		a_info = getaddrinfo(hname, service, &hints, &ai);	
 538	}
 539	if (a_info != 0) {
 540		(void) fprintf(stderr, "%s\n", gai_strerror(a_info));
 541		if (ai != NULL)
 542			freeaddrinfo(ai);
 543		return 0;
 544	}
 545
 546	if (ai->ai_canonname == NULL) {
 547		strncpy(temphost, stoa((struct sockaddr_storage *)ai->ai_addr),
 548		    LENHOSTNAME);
 549		temphost[LENHOSTNAME-1] = '\0';
 550	} else {
 551		strncpy(temphost, ai->ai_canonname, LENHOSTNAME);
 552		temphost[LENHOSTNAME-1] = '\0';
 553	}
 554
 555	if (debug > 2)
 556	    printf("Opening host %s\n", temphost);
 557
 558	if (havehost == 1) {
 559		if (debug > 2)
 560		    printf("Closing old host %s\n", currenthost);
 561		(void) closesocket(sockfd);
 562		havehost = 0;
 563	}
 564	(void) strcpy(currenthost, temphost);
 565	
 566	/* port maps to the same in both families */
 567	s_port = ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port; 
 568#ifdef SYS_VXWORKS
 569	((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM);
 570	if (ai->ai_family == AF_INET)
 571		*(struct sockaddr_in *)&hostaddr= 
 572			*((struct sockaddr_in *)ai->ai_addr);
 573	else 
 574		*(struct sockaddr_in6 *)&hostaddr= 
 575			*((struct sockaddr_in6 *)ai->ai_addr);
 576#endif /* SYS_VXWORKS */
 577
 578#ifdef SYS_WINNT
 579	{
 580		int optionValue = SO_SYNCHRONOUS_NONALERT;
 581		int err;
 582
 583		err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&optionValue, sizeof(optionValue));
 584		if (err != NO_ERROR) {
 585			(void) fprintf(stderr, "cannot open nonoverlapped sockets\n");
 586			exit(1);
 587		}
 588	}
 589
 590	sockfd = socket(ai->ai_family, SOCK_DGRAM, 0);
 591	if (sockfd == INVALID_SOCKET) {
 592		error("socket", "", "");
 593		exit(-1);
 594	}
 595#else
 596	sockfd = socket(ai->ai_family, SOCK_DGRAM, 0);
 597	if (sockfd == -1)
 598	    error("socket", "", "");
 599#endif /* SYS_WINNT */
 600
 601	
 602#ifdef NEED_RCVBUF_SLOP
 603# ifdef SO_RCVBUF
 604	{
 605		int rbufsize = INITDATASIZE + 2048; /* 2K for slop */
 606
 607		if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
 608			       &rbufsize, sizeof(int)) == -1)
 609		    error("setsockopt", "", "");
 610	}
 611# endif
 612#endif
 613
 614#ifdef SYS_VXWORKS
 615	if (connect(sockfd, (struct sockaddr *)&hostaddr, 
 616		    sizeof(hostaddr)) == -1)
 617#else
 618	if (connect(sockfd, (struct sockaddr *)ai->ai_addr,
 619		    ai->ai_addrlen) == -1)
 620#endif /* SYS_VXWORKS */
 621	    error("connect", "", "");
 622	if (ai != NULL)
 623		freeaddrinfo(ai);
 624	havehost = 1;
 625	req_pkt_size = REQ_LEN_NOMAC;
 626	impl_ver = IMPL_XNTPD;
 627	return 1;
 628}
 629
 630
 631/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
 632/*
 633 * sendpkt - send a packet to the remote host
 634 */
 635static int
 636sendpkt(
 637	char *xdata,
 638	int xdatalen
 639	)
 640{
 641	if (send(sockfd, xdata, (size_t)xdatalen, 0) == -1) {
 642		warning("write to %s failed", currenthost, "");
 643		return -1;
 644	}
 645
 646	return 0;
 647}
 648
 649
 650/*
 651 * growpktdata - grow the packet data area
 652 */
 653static void
 654growpktdata(void)
 655{
 656	pktdatasize += INCDATASIZE;
 657	pktdata = (char *)realloc(pktdata, (unsigned)pktdatasize);
 658	if (pktdata == 0) {
 659		(void) fprintf(stderr, "%s: realloc() failed!\n", progname);
 660		exit(1);
 661	}
 662}
 663
 664
 665/*
 666 * getresponse - get a (series of) response packet(s) and return the data
 667 */
 668static int
 669getresponse(
 670	int implcode,
 671	int reqcode,
 672	int *ritems,
 673	int *rsize,
 674	char **rdata,
 675	int esize
 676	)
 677{
 678	struct resp_pkt rpkt;
 679	struct timeval tvo;
 680	int items;
 681	int i;
 682	int size;
 683	int datasize;
 684	char *datap;
 685	char *tmp_data;
 686	char haveseq[MAXSEQ+1];
 687	int firstpkt;
 688	int lastseq;
 689	int numrecv;
 690	int seq;
 691	fd_set fds;
 692	int n;
 693	int pad;
 694
 695	/*
 696	 * This is pretty tricky.  We may get between 1 and many packets
 697	 * back in response to the request.  We peel the data out of
 698	 * each packet and collect it in one long block.  When the last
 699	 * packet in the sequence is received we'll know how many we
 700	 * should have had.  Note we use one long time out, should reconsider.
 701	 */
 702	*ritems = 0;
 703	*rsize = 0;
 704	firstpkt = 1;
 705	numrecv = 0;
 706	*rdata = datap = pktdata;
 707	lastseq = 999;	/* too big to be a sequence number */
 708	memset(haveseq, 0, sizeof(haveseq));
 709	FD_ZERO(&fds);
 710
 711    again:
 712	if (firstpkt)
 713	    tvo = tvout;
 714	else
 715	    tvo = tvsout;
 716	
 717	FD_SET(sockfd, &fds);
 718	n = select(sockfd+1, &fds, (fd_set *)0, (fd_set *)0, &tvo);
 719
 720	if (n == -1) {
 721		warning("select fails", "", "");
 722		return -1;
 723	}
 724	if (n == 0) {
 725		/*
 726		 * Timed out.  Return what we have
 727		 */
 728		if (firstpkt) {
 729			(void) fprintf(stderr,
 730				       "%s: timed out, nothing received\n", currenthost);
 731			return ERR_TIMEOUT;
 732		} else {
 733			(void) fprintf(stderr,
 734				       "%s: timed out with incomplete data\n",
 735				       currenthost);
 736			if (debug) {
 737				printf("Received sequence numbers");
 738				for (n = 0; n <= MAXSEQ; n++)
 739				    if (haveseq[n])
 740					printf(" %d,", n);
 741				if (lastseq != 999)
 742				    printf(" last frame received\n");
 743				else
 744				    printf(" last frame not received\n");
 745			}
 746			return ERR_INCOMPLETE;
 747		}
 748	}
 749
 750	n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
 751	if (n == -1) {
 752		warning("read", "", "");
 753		return -1;
 754	}
 755
 756
 757	/*
 758	 * Check for format errors.  Bug proofing.
 759	 */
 760	if (n < RESP_HEADER_SIZE) {
 761		if (debug)
 762		    printf("Short (%d byte) packet received\n", n);
 763		goto again;
 764	}
 765	if (INFO_VERSION(rpkt.rm_vn_mode) > NTP_VERSION ||
 766	    INFO_VERSION(rpkt.rm_vn_mode) < NTP_OLDVERSION) {
 767		if (debug)
 768		    printf("Packet received with version %d\n",
 769			   INFO_VERSION(rpkt.rm_vn_mode));
 770		goto again;
 771	}
 772	if (INFO_MODE(rpkt.rm_vn_mode) != MODE_PRIVATE) {
 773		if (debug)
 774		    printf("Packet received with mode %d\n",
 775			   INFO_MODE(rpkt.rm_vn_mode));
 776		goto again;
 777	}
 778	if (INFO_IS_AUTH(rpkt.auth_seq)) {
 779		if (debug)
 780		    printf("Encrypted packet received\n");
 781		goto again;
 782	}
 783	if (!ISRESPONSE(rpkt.rm_vn_mode)) {
 784		if (debug)
 785		    printf("Received request packet, wanted response\n");
 786		goto again;
 787	}
 788	if (INFO_MBZ(rpkt.mbz_itemsize) != 0) {
 789		if (debug)
 790		    printf("Received packet with nonzero MBZ field!\n");
 791		goto again;
 792	}
 793
 794	/*
 795	 * Check implementation/request.  Could be old data getting to us.
 796	 */
 797	if (rpkt.implementation != implcode || rpkt.request != reqcode) {
 798		if (debug)
 799		    printf(
 800			    "Received implementation/request of %d/%d, wanted %d/%d",
 801			    rpkt.implementation, rpkt.request,
 802			    implcode, reqcode);
 803		goto again;
 804	}
 805
 806	/*
 807	 * Check the error code.  If non-zero, return it.
 808	 */
 809	if (INFO_ERR(rpkt.err_nitems) != INFO_OKAY) {
 810		if (debug && ISMORE(rpkt.rm_vn_mode)) {
 811			printf("Error code %d received on not-final packet\n",
 812			       INFO_ERR(rpkt.err_nitems));
 813		}
 814		return (int)INFO_ERR(rpkt.err_nitems);
 815	}
 816
 817	/*
 818	 * Collect items and size.  Make sure they make sense.
 819	 */
 820	items = INFO_NITEMS(rpkt.err_nitems);
 821	size = INFO_ITEMSIZE(rpkt.mbz_itemsize);
 822	if (esize > size)
 823		pad = esize - size;
 824	else 
 825		pad = 0;
 826	if ((datasize = items*size) > (n-RESP_HEADER_SIZE)) {
 827		if (debug)
 828		    printf(
 829			    "Received items %d, size %d (total %d), data in packet is %d\n",
 830			    items, size, datasize, n-RESP_HEADER_SIZE);
 831		goto again;
 832	}
 833
 834	/*
 835	 * If this isn't our first packet, make sure the size matches
 836	 * the other ones.
 837	 */
 838	if (!firstpkt && esize != *rsize) {
 839		if (debug)
 840		    printf("Received itemsize %d, previous %d\n",
 841			   size, *rsize);
 842		goto again;
 843	}
 844	/*
 845	 * If we've received this before, +toss it
 846	 */
 847	seq = INFO_SEQ(rpkt.auth_seq);
 848	if (haveseq[seq]) {
 849		if (debug)
 850		    printf("Received duplicate sequence number %d\n", seq);
 851		goto again;
 852	}
 853	haveseq[seq] = 1;
 854
 855	/*
 856	 * If this is the last in the sequence, record that.
 857	 */
 858	if (!ISMORE(rpkt.rm_vn_mode)) {
 859		if (lastseq != 999) {
 860			printf("Received second end sequence packet\n");
 861			goto again;
 862		}
 863		lastseq = seq;
 864	}
 865
 866	/*
 867	 * So far, so good.  Copy this data into the output array.
 868	 */
 869	if ((datap + datasize + (pad * items)) > (pktdata + pktdatasize)) {
 870		int offset = datap - pktdata;
 871		growpktdata();
 872	        *rdata = pktdata; /* might have been realloced ! */
 873		datap = pktdata + offset;
 874	}
 875	/* 
 876	 * We now move the pointer along according to size and number of
 877	 * items.  This is so we can play nice with older implementations
 878	 */
 879
 880	tmp_data = (char *)rpkt.data;
 881	for(i = 0; i <items; i++){
 882		memmove(datap, tmp_data, (unsigned)size);
 883		tmp_data += size;
 884		memset(datap + size, 0, pad);
 885		datap += size + pad;
 886	}
 887
 888	if (firstpkt) {
 889		firstpkt = 0;
 890		*rsize = size + pad;
 891	}
 892	*ritems += items;
 893
 894	/*
 895	 * Finally, check the count of received packets.  If we've got them
 896	 * all, return
 897	 */
 898	++numrecv;
 899	if (numrecv <= lastseq)
 900	    goto again;
 901	return INFO_OKAY;
 902}
 903
 904
 905/*
 906 * sendrequest - format and send a request packet
 907 */
 908static int
 909sendrequest(
 910	int implcode,
 911	int reqcode,
 912	int auth,
 913	int qitems,
 914	int qsize,
 915	char *qdata
 916	)
 917{
 918	struct req_pkt qpkt;
 919	int datasize;
 920
 921	memset((char *)&qpkt, 0, sizeof qpkt);
 922
 923	qpkt.rm_vn_mode = RM_VN_MODE(0, 0, 0);
 924	qpkt.implementation = (u_char)implcode;
 925	qpkt.request = (u_char)reqcode;
 926
 927	datasize = qitems * qsize;
 928	if (datasize != 0 && qdata != NULL) {
 929		memmove((char *)qpkt.data, qdata, (unsigned)datasize);
 930		qpkt.err_nitems = ERR_NITEMS(0, qitems);
 931		qpkt.mbz_itemsize = MBZ_ITEMSIZE(qsize);
 932	} else {
 933		qpkt.err_nitems = ERR_NITEMS(0, 0);
 934		qpkt.mbz_itemsize = MBZ_ITEMSIZE(qsize);  /* allow for optional first item */
 935	}
 936
 937	if (!auth || (keyid_entered && info_auth_keyid == 0)) {
 938		qpkt.auth_seq = AUTH_SEQ(0, 0);
 939		return sendpkt((char *)&qpkt, req_pkt_size);
 940	} else {
 941		l_fp ts;
 942		int maclen = 0;
 943		const char *pass = "\0";
 944		struct req_pkt_tail *qpktail;
 945
 946		qpktail = (struct req_pkt_tail *)((char *)&qpkt + req_pkt_size
 947		    + MAX_MAC_LEN - sizeof(struct req_pkt_tail));
 948
 949		if (info_auth_keyid == 0) {
 950			if (((struct conf_peer *)qpkt.data)->keyid > 0)
 951				info_auth_keyid = ((struct conf_peer *)qpkt.data)->keyid;
 952			else {
 953				maclen = getkeyid("Keyid: ");
 954				if (maclen == 0) {
 955					(void) fprintf(stderr,
 956					    "Invalid key identifier\n");
 957					return 1;
 958				}
 959				info_auth_keyid = maclen;
 960			}
 961		}
 962		if (!authistrusted(info_auth_keyid)) {
 963			pass = getpass("MD5 Password: ");
 964			if (*pass == '\0') {
 965				(void) fprintf(stderr,
 966				    "Invalid password\n");
 967				return (1);
 968			}
 969		}
 970		authusekey(info_auth_keyid, info_auth_keytype, (const u_char *)pass);
 971		authtrust(info_auth_keyid, 1);
 972		qpkt.auth_seq = AUTH_SEQ(1, 0);
 973		qpktail->keyid = htonl(info_auth_keyid);
 974		get_systime(&ts);
 975		L_ADD(&ts, &delay_time);
 976		HTONL_FP(&ts, &qpktail->tstamp);
 977		maclen = authencrypt(info_auth_keyid, (u_int32 *)&qpkt,
 978		    req_pkt_size);
 979		if (maclen == 0) {  
 980			(void) fprintf(stderr, "Key not found\n");
 981			return (1);
 982		}
 983		return sendpkt((char *)&qpkt, (int)(req_pkt_size + maclen));
 984	}
 985	/*NOTREACHED*/
 986}
 987
 988
 989/*
 990 * doquery - send a request and process the response
 991 */
 992int
 993doquery(
 994	int implcode,
 995	int reqcode,
 996	int auth,
 997	int qitems,
 998	int qsize,
 999	char *qdata,
1000	int *ritems,
1001	int *rsize,
1002	char **rdata,
1003 	int quiet_mask,
1004	int esize
1005	)
1006{
1007	int res;
1008	char junk[512];
1009	fd_set fds;
1010	struct timeval tvzero;
1011
1012	/*
1013	 * Check to make sure host is open
1014	 */
1015	if (!havehost) {
1016		(void) fprintf(stderr, "***No host open, use `host' command\n");
1017		return -1;
1018	}
1019
1020	/*
1021	 * Poll the socket and clear out any pending data
1022	 */
1023again:
1024	do {
1025		tvzero.tv_sec = tvzero.tv_usec = 0;
1026		FD_ZERO(&fds);
1027		FD_SET(sockfd, &fds);
1028		res = select(sockfd+1, &fds, (fd_set *)0, (fd_set *)0, &tvzero);
1029
1030		if (res == -1) {
1031			warning("polling select", "", "");
1032			return -1;
1033		} else if (res > 0)
1034
1035		    (void) recv(sockfd, junk, sizeof junk, 0);
1036	} while (res > 0);
1037
1038
1039	/*
1040	 * send a request
1041	 */
1042	res = sendrequest(implcode, reqcode, auth, qitems, qsize, qdata);
1043	if (res != 0)
1044	    return res;
1045	
1046	/*
1047	 * Get the response.  If we got a standard error, print a message
1048	 */
1049	res = getresponse(implcode, reqcode, ritems, rsize, rdata, esize);
1050
1051	/*
1052	 * Try to be compatible with older implementations of ntpd.
1053	 */
1054	if (res == INFO_ERR_FMT && req_pkt_size != 48) {
1055		int oldsize;
1056
1057		oldsize = req_pkt_size;
1058
1059		switch(req_pkt_size) {
1060		case REQ_LEN_NOMAC:
1061			req_pkt_size = 160;
1062			break;
1063		case 160:
1064			req_pkt_size = 48;
1065			break;
1066		}
1067		if (impl_ver == IMPL_XNTPD) {
1068			fprintf(stderr,
1069			    "***Warning changing to older implementation\n");
1070			return INFO_ERR_IMPL;
1071		}
1072
1073		fprintf(stderr,
1074		    "***Warning changing the request packet size from %d to %d\n",
1075		    oldsize, req_pkt_size);
1076		goto again;
1077	}
1078
1079 	/* log error message if not told to be quiet */
1080 	if ((res > 0) && (((1 << res) & quiet_mask) == 0)) {
1081		switch(res) {
1082		    case INFO_ERR_IMPL:
1083			/* Give us a chance to try the older implementation. */
1084			if (implcode == IMPL_XNTPD)
1085				break;
1086			(void) fprintf(stderr,
1087				       "***Server implementation incompatable with our own\n");
1088			break;
1089		    case INFO_ERR_REQ:
1090			(void) fprintf(stderr,
1091				       "***Server doesn't implement this request\n");
1092			break;
1093		    case INFO_ERR_FMT:
1094			(void) fprintf(stderr,
1095				       "***Server reports a format error in the received packet (shouldn't happen)\n");
1096			break;
1097		    case INFO_ERR_NODATA:
1098			(void) fprintf(stderr,
1099				       "***Server reports data not found\n");
1100			break;
1101		    case INFO_ERR_AUTH:
1102			(void) fprintf(stderr, "***Permission denied\n");
1103			break;
1104		    case ERR_TIMEOUT:
1105			(void) fprintf(stderr, "***Request timed out\n");
1106			break;
1107		    case ERR_INCOMPLETE:
1108			(void) fprintf(stderr,
1109				       "***Response from server was incomplete\n");
1110			break;
1111		    default:
1112			(void) fprintf(stderr,
1113				       "***Server returns unknown error code %d\n", res);
1114			break;
1115		}
1116	}
1117	return res;
1118}
1119
1120
1121/*
1122 * getcmds - read commands from the standard input and execute them
1123 */
1124static void
1125getcmds(void)
1126{
1127#if defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDIT)
1128	char *line;
1129
1130	for (;;) {
1131		if ((line = readline(interactive?prompt:"")) == NULL) return;
1132		if (*line) add_history(line);
1133		docmd(line);
1134		free(line);
1135	}
1136#else /* not (HAVE_LIBREADLINE || HAVE_LIBEDIT) */
1137	char line[MAXLINE];
1138
1139	for (;;) {
1140		if (interactive) {
1141#ifdef VMS	/* work around a problem with mixing stdout & stderr */
1142			fputs("",stdout);
1143#endif
1144			(void) fputs(prompt, stderr);
1145			(void) fflush(stderr);
1146		}
1147
1148		if (fgets(line, sizeof line, stdin) == NULL)
1149		    return;
1150
1151		docmd(line);
1152	}
1153#endif /* not HAVE_LIBREADLINE || HAVE_LIBEDIT */
1154}
1155
1156
1157#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
1158/*
1159 * abortcmd - catch interrupts and abort the current command
1160 */
1161static RETSIGTYPE
1162abortcmd(
1163	int sig
1164	)
1165{
1166
1167	if (current_output == stdout)
1168	    (void) fflush(stdout);
1169	putc('\n', stderr);
1170	(void) fflush(stderr);
1171	if (jump) longjmp(interrupt_buf, 1);
1172}
1173#endif /* SYS_WINNT */
1174
1175/*
1176 * docmd - decode the command line and execute a command
1177 */
1178static void
1179docmd(
1180	const char *cmdline
1181	)
1182{
1183	char *tokens[1+MAXARGS+MOREARGS+2];
1184	struct parse pcmd;
1185	int ntok;
1186	int i, ti;
1187	int rval;
1188	struct xcmd *xcmd;
1189
1190	ai_fam_templ = ai_fam_default;
1191	/*
1192	 * Tokenize the command line.  If nothing on it, return.
1193	 */
1194	tokenize(cmdline, tokens, &ntok);
1195	if (ntok == 0)
1196	    return;
1197	
1198	/*
1199	 * Find the appropriate command description.
1200	 */
1201	i = findcmd(tokens[0], builtins, opcmds, &xcmd);
1202	if (i == 0) {
1203		(void) fprintf(stderr, "***Command `%s' unknown\n",
1204			       tokens[0]);
1205		return;
1206	} else if (i >= 2) {
1207		(void) fprintf(stderr, "***Command `%s' ambiguous\n",
1208			       tokens[0]);
1209		return;
1210	}
1211	
1212	/*
1213	 * Save the keyword, then walk through the arguments, interpreting
1214	 * as we go.
1215	 */
1216	pcmd.keyword = tokens[0];
1217	pcmd.nargs = 0;
1218	ti = 1;
1219	for (i = 0; i < MAXARGS && xcmd->arg[i] != NO;) {
1220		if ((i+ti) >= ntok) {
1221			if (!(xcmd->arg[i] & OPT)) {
1222				printusage(xcmd, stderr);
1223				return;
1224			}
1225			break;
1226		}
1227		if ((xcmd->arg[i] & OPT) && (*tokens[i+ti] == '>'))
1228			break;
1229		rval = getarg(tokens[i+ti], (int)xcmd->arg[i], &pcmd.argval[i]);
1230		if (rval == -1) {
1231			ti++;
1232			continue;
1233		}
1234		if (rval == 0)
1235			return;
1236		pcmd.nargs++;
1237		i++;
1238	}
1239
1240	/* Any extra args are assumed to be "OPT|NTP_STR". */
1241	for ( ; i < MAXARGS + MOREARGS;) {
1242	     if ((i+ti) >= ntok)
1243		  break;
1244		rval = getarg(tokens[i+ti], (int)(OPT|NTP_STR), &pcmd.argval[i]);
1245		if (rval == -1) {
1246			ti++;
1247			continue;
1248		}
1249		if (rval == 0)
1250			return;
1251		pcmd.nargs++;
1252		i++;
1253	}
1254
1255	i += ti;
1256	if (i < ntok && *tokens[i] == '>') {
1257		char *fname;
1258
1259		if (*(tokens[i]+1) != '\0')
1260		    fname = tokens[i]+1;
1261		else if ((i+1) < ntok)
1262		    fname = tokens[i+1];
1263		else {
1264			(void) fprintf(stderr, "***No file for redirect\n");
1265			return;
1266		}
1267
1268		current_output = fopen(fname, "w");
1269		if (current_output == NULL) {
1270			(void) fprintf(stderr, "***Error opening %s: ", fname);
1271			perror("");
1272			return;
1273		}
1274	} else {
1275		current_output = stdout;
1276	}
1277
1278	if (interactive && setjmp(interrupt_buf)) {
1279		return;
1280	} else {
1281		jump = 1;
1282		(xcmd->handler)(&pcmd, current_output);
1283		jump = 0;
1284		if (current_output != stdout)
1285			(void) fclose(current_output);
1286		current_output = NULL;
1287	}
1288}
1289
1290
1291/*
1292 * tokenize - turn a command line into tokens
1293 */
1294static void
1295tokenize(
1296	const char *line,
1297	char **tokens,
1298	int *ntok
1299	)
1300{
1301	register const char *cp;
1302	register char *sp;
1303	static char tspace[MAXLINE];
1304
1305	sp = tspace;
1306	cp = line;
1307	for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
1308		tokens[*ntok] = sp;
1309		while (ISSPACE(*cp))
1310		    cp++;
1311		if (ISEOL(*cp))
1312		    break;
1313		do {
1314			*sp++ = *cp++;
1315		} while (!ISSPACE(*cp) && !ISEOL(*cp));
1316
1317		*sp++ = '\0';
1318	}
1319}
1320
1321
1322
1323/*
1324 * findcmd - find a command in a command description table
1325 */
1326static int
1327findcmd(
1328	register char *str,
1329	struct xcmd *clist1,
1330	struct xcmd *clist2,
1331	struct xcmd **cmd
1332	)
1333{
1334	register struct xcmd *cl;
1335	register int clen;
1336	int nmatch;
1337	struct xcmd *nearmatch = NULL;
1338	struct xcmd *clist;
1339
1340	clen = strlen(str);
1341	nmatch = 0;
1342	if (clist1 != 0)
1343	    clist = clist1;
1344	else if (clist2 != 0)
1345	    clist = clist2;
1346	else
1347	    return 0;
1348
1349    again:
1350	for (cl = clist; cl->keyword != 0; cl++) {
1351		/* do a first character check, for efficiency */
1352		if (*str != *(cl->keyword))
1353		    continue;
1354		if (strncmp(str, cl->keyword, (unsigned)clen) == 0) {
1355			/*
1356			 * Could be extact match, could be approximate.
1357			 * Is exact if the length of the keyword is the
1358			 * same as the str.
1359			 */
1360			if (*((cl->keyword) + clen) == '\0') {
1361				*cmd = cl;
1362				return 1;
1363			}
1364			nmatch++;
1365			nearmatch = cl;
1366		}
1367	}
1368
1369				/*
1370				 * See if there is more to do.  If so, go again.  Sorry about the
1371				 * goto, too much looking at BSD sources...
1372				 */
1373	if (clist == clist1 && clist2 != 0) {
1374		clist = clist2;
1375		goto again;
1376	}
1377
1378				/*
1379				 * If we got extactly 1 near match, use it, else return number
1380				 * of matches.
1381				 */
1382	if (nmatch == 1) {
1383		*cmd = nearmatch;
1384		return 1;
1385	}
1386	return nmatch;
1387}
1388
1389
1390/*
1391 * getarg - interpret an argument token
1392 *
1393 * string is always set.
1394 * type is set to the decoded type.
1395 *
1396 * return:	 0 - failure
1397 *		 1 - success
1398 *		-1 - skip to next token
1399 */
1400static int
1401getarg(
1402	char *str,
1403	int code,
1404	arg_v *argp
1405	)
1406{
1407	int isneg;
1408	char *cp, *np;
1409	static const char *digits = "0123456789";
1410
1411	memset(argp, 0, sizeof(*argp));
1412
1413	argp->string = str;
1414	argp->type   = code & ~OPT;
1415
1416	switch (argp->type) {
1417	    case NTP_STR:
1418		break;
1419	    case NTP_ADD:
1420		if (!strcmp("-6", str)) {
1421			ai_fam_templ = AF_INET6;
1422			return -1;
1423		} else if (!strcmp("-4", str)) {
1424			ai_fam_templ = AF_INET;
1425			return -1;
1426		}
1427		if (!getnetnum(str, &(argp->netnum), (char *)0, 0)) {
1428			return 0;
1429		}
1430		break;
1431	    case NTP_INT:
1432	    case NTP_UINT:
1433		isneg = 0;
1434		np = str;
1435		if (*np == '-') {
1436			np++;
1437			isneg = 1;
1438		}
1439
1440		argp->uval = 0;
1441		do {
1442			cp = strchr(digits, *np);
1443			if (cp == NULL) {
1444				(void) fprintf(stderr,
1445					       "***Illegal integer value %s\n", str);
1446				return 0;
1447			}
1448			argp->uval *= 10;
1449			argp->uval += (cp - digits);
1450		} while (*(++np) != '\0');
1451
1452		if (isneg) {
1453			if ((code & ~OPT) == NTP_UINT) {
1454				(void) fprintf(stderr,
1455					       "***Value %s should be unsigned\n", str);
1456				return 0;
1457			}
1458			argp->ival = -argp->ival;
1459		}
1460		break;
1461	    case IP_VERSION:
1462		if (!strcmp("-6", str))
1463			argp->ival = 6 ;
1464		else if (!strcmp("-4", str))
1465			argp->ival = 4 ;
1466		else {
1467			(void) fprintf(stderr,
1468			    "***Version must be either 4 or 6\n");
1469			return 0;
1470		}
1471		break;
1472	}
1473
1474	return 1;
1475}
1476
1477
1478/*
1479 * getnetnum - given a host name, return its net number
1480 *	       and (optional) full name
1481 */
1482static int
1483getnetnum(
1484	const char *hname,
1485	struct sockaddr_storage *num,
1486	char *fullhost,
1487	int af
1488	)
1489{
1490	int sockaddr_len;
1491	struct addrinfo hints, *ai = NULL;
1492
1493	sockaddr_len = (af == AF_INET)
1494			   ? sizeof(struct sockaddr_in)
1495			   : sizeof(struct sockaddr_in6);
1496	memset((char *)&hints, 0, sizeof(struct addrinfo));
1497	hints.ai_flags = AI_CANONNAME;
1498#ifdef AI_ADDRCONFIG
1499	hints.ai_flags |= AI_ADDRCONFIG;
1500#endif
1501	
1502	/* decodenetnum only works with addresses */
1503	if (decodenetnum(hname, num)) {
1504		if (fullhost != 0) {
1505			getnameinfo((struct sockaddr *)num, sockaddr_len, 
1506				    fullhost, sizeof(fullhost), NULL, 0, 
1507				    NI_NUMERICHOST); 
1508		}
1509		return 1;
1510	} else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) {
1511		memmove((char *)num, ai->ai_addr, ai->ai_addrlen);
1512		if (fullhost != 0)
1513			(void) strcpy(fullhost, ai->ai_canonname);
1514		return 1;
1515	} else {
1516		(void) fprintf(stderr, "***Can't find host %s\n", hname);
1517		return 0;
1518	}
1519	/*NOTREACHED*/
1520}
1521
1522/*
1523 * nntohost - convert network number to host name.  This routine enforces
1524 *	       the showhostnames setting.
1525 */
1526char *
1527nntohost(
1528	struct sockaddr_storage *netnum
1529	)
1530{
1531	if (!showhostnames)
1532	    return stoa(netnum);
1533
1534	if ((netnum->ss_family == AF_INET) && ISREFCLOCKADR(netnum))
1535		return refnumtoa(netnum);
1536	return socktohost(netnum);
1537}
1538
1539
1540/*
1541 * Finally, the built in command handlers
1542 */
1543
1544/*
1545 * help - tell about commands, or details of a particular command
1546 */
1547static void
1548help(
1549	struct parse *pcmd,
1550	FILE *fp
1551	)
1552{
1553	struct xcmd *xcp;
1554	char *cmd;
1555	const char *list[100];
1556	int word, words;     
1557        int row, rows;
1558	int col, cols;
1559
1560	if (pcmd->nargs == 0) {
1561		words = 0;
1562		for (xcp = builtins; xcp->keyword != 0; xcp++) {
1563			if (*(xcp->keyword) != '?')
1564			    list[words++] = xcp->keyword;
1565		}
1566                for (xcp = opcmds; xcp->keyword != 0; xcp++)
1567		    list[words++] = xcp->keyword;
1568
1569		qsort(
1570#ifdef QSORT_USES_VOID_P
1571		    (void *)
1572#else
1573		    (char *)
1574#endif
1575			(list), (size_t)(words), sizeof(char *), helpsort);
1576		col = 0;
1577		for (word = 0; word < words; word++) {
1578			int length = strlen(list[word]);
1579			if (col < length) {
1580			    col = length;
1581                        }
1582		}
1583
1584		cols = SCREENWIDTH / ++col;
1585                rows = (words + cols - 1) / cols;
1586
1587		(void) fprintf(fp, "ntpdc commands:\n");
1588
1589		for (row = 0; row < rows; row++) {
1590                        for (word = row; word < words; word += rows) {
1591				(void) fprintf(fp, "%-*.*s", col, col-1, list[word]);
1592                        }
1593			(void) fprintf(fp, "\n");
1594		}
1595	} else {
1596		cmd = pcmd->argval[0].string;
1597		words = findcmd(cmd, builtins, opcmds, &xcp);
1598		if (words == 0) {
1599			(void) fprintf(stderr,
1600				       "Command `%s' is unknown\n", cmd);
1601			return;
1602		} else if (words >= 2) {
1603			(void) fprintf(stderr,
1604				       "Command `%s' is ambiguous\n", cmd);
1605			return;
1606		}
1607		(void) fprintf(fp, "function: %s\n", xcp->comment);
1608		printusage(xcp, fp);
1609	}
1610}
1611
1612
1613/*
1614 * helpsort - do hostname qsort comparisons
1615 */
1616#ifdef QSORT_USES_VOID_P
1617static int
1618helpsort(
1619	const void *t1,
1620	const void *t2
1621	)
1622{
1623	char const * const * name1 = (char const * const *)t1;
1624	char const * const * name2 = (char const * const *)t2;
1625
1626	return strcmp(*name1, *name2);
1627}
1628#else
1629static int
1630helpsort(
1631	char **name1,
1632	char **name2
1633	)
1634{
1635	return strcmp(*name1, *name2);
1636}
1637#endif
1638
1639
1640/*
1641 * printusage - print usage information for a command
1642 */
1643static void
1644printusage(
1645	struct xcmd *xcp,
1646	FILE *fp
1647	)
1648{
1649	int i, opt46;
1650
1651	opt46 = 0;
1652	(void) fprintf(fp, "usage: %s", xcp->keyword);
1653	for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
1654		if (opt46 == 0 && (xcp->arg[i] & ~OPT) == NTP_ADD) {
1655			(void) fprintf(fp, " [ -4|-6 ]");
1656			opt46 = 1;
1657		}
1658		if (xcp->arg[i] & OPT)
1659		    (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
1660		else
1661		    (void) fprintf(fp, " %s", xcp->desc[i]);
1662	}
1663	(void) fprintf(fp, "\n");
1664}
1665
1666
1667/*
1668 * timeout - set time out time
1669 */
1670static void
1671timeout(
1672	struct parse *pcmd,
1673	FILE *fp
1674	)
1675{
1676	int val;
1677
1678	if (pcmd->nargs == 0) {
1679		val = tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
1680		(void) fprintf(fp, "primary timeout %d ms\n", val);
1681	} else {
1682		tvout.tv_sec = pcmd->argval[0].uval / 1000;
1683		tvout.tv_usec = (pcmd->argval[0].uval - (tvout.tv_sec * 1000))
1684			* 1000;
1685	}
1686}
1687
1688
1689/*
1690 * my_delay - set delay for auth requests
1691 */
1692static void
1693my_delay(
1694	struct parse *pcmd,
1695	FILE *fp
1696	)
1697{
1698	int isneg;
1699	u_long val;
1700
1701	if (pcmd->nargs == 0) {
1702		val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
1703		(void) fprintf(fp, "delay %lu ms\n", val);
1704	} else {
1705		if (pcmd->argval[0].ival < 0) {
1706			isneg = 1;
1707			val = (u_long)(-pcmd->argval[0].ival);
1708		} else {
1709			isneg = 0;
1710			val = (u_long)pcmd->argval[0].ival;
1711		}
1712
1713		delay_time.l_ui = val / 1000;
1714		val %= 1000;
1715		delay_time.l_uf = val * 4294967;	/* 2**32/1000 */
1716
1717		if (isneg)
1718		    L_NEG(&delay_time);
1719	}
1720}
1721
1722
1723/*
1724 * host - set the host we are dealing with.
1725 */
1726static void
1727host(
1728	struct parse *pcmd,
1729	FILE *fp
1730	)
1731{
1732	int i;
1733
1734	if (pcmd->nargs == 0) {
1735		if (havehost)
1736		    (void) fprintf(fp, "current host is %s\n", currenthost);
1737		else
1738		    (void) fprintf(fp, "no current host\n");
1739		return;
1740	}
1741
1742	i = 0;
1743	if (pcmd->nargs == 2) {
1744		if (!strcmp("-4", pcmd->argval[i].string))
1745			ai_fam_templ = AF_INET;
1746		else if (!strcmp("-6", pcmd->argval[i].string))
1747			ai_fam_templ = AF_INET6;
1748		else {
1749			if (havehost)
1750				(void) fprintf(fp,
1751				    "current host remains %s\n", currenthost);
1752			else
1753				(void) fprintf(fp, "still no current host\n");
1754			return;
1755		}
1756		i = 1;
1757	}
1758	if (openhost(pcmd->argval[i].string)) {
1759		(void) fprintf(fp, "current host set to %s\n", currenthost);
1760	} else {
1761		if (havehost)
1762		    (void) fprintf(fp,
1763				   "current host remains %s\n", currenthost);
1764		else
1765		    (void) fprintf(fp, "still no current host\n");
1766	}
1767}
1768
1769
1770/*
1771 * keyid - get a keyid to use for authenticating requests
1772 */
1773static void
1774keyid(
1775	struct parse *pcmd,
1776	FILE *fp
1777	)
1778{
1779	if (pcmd->nargs == 0) {
1780		if (info_auth_keyid == 0 && !keyid_entered)
1781		    (void) fprintf(fp, "no keyid defined\n");
1782		else if (info_auth_keyid == 0 && keyid_entered)
1783		    (void) fprintf(fp, "no keyid will be sent\n");
1784		else
1785		    (void) fprintf(fp, "keyid is %lu\n", (u_long)info_auth_keyid);
1786	} else {
1787		info_auth_keyid = pcmd->argval[0].uval;
1788		keyid_entered = 1;
1789	}
1790}
1791
1792
1793/*
1794 * keytype - get type of key to use for authenticating requests
1795 */
1796static void
1797keytype(
1798	struct parse *pcmd,
1799	FILE *fp
1800	)
1801{
1802	if (pcmd->nargs == 0)
1803	    fprintf(fp, "keytype is %s\n",
1804		    (info_auth_keytype == KEY_TYPE_MD5) ? "MD5" : "???");
1805	else
1806	    switch (*(pcmd->argval[0].string)) {
1807		case 'm':
1808		case 'M':
1809		    info_auth_keytype = KEY_TYPE_MD5;
1810		    break;
1811
1812		default:
1813		    fprintf(fp, "keytype must be 'md5'\n");
1814	    }
1815}
1816
1817
1818
1819/*
1820 * passwd - get an authentication key
1821 */
1822/*ARGSUSED*/
1823static void
1824passwd(
1825	struct parse *pcmd,
1826	FILE *fp
1827	)
1828{
1829	char *pass;
1830
1831	if (info_auth_keyid == 0) {
1832		info_auth_keyid = getkeyid("Keyid: ");
1833		if (info_auth_keyid == 0) {
1834			(void)fprintf(fp, "Keyid must be defined\n");
1835			return;
1836		}
1837	}
1838	if (!interactive) {
1839		authusekey(info_auth_keyid, info_auth_keytype,
1840			   (u_char *)pcmd->argval[0].string);
1841		authtrust(info_auth_keyid, 1);
1842	} else {
1843		pass = getpass("MD5 Password: ");
1844		if (*pass == '\0')
1845		    (void) fprintf(fp, "Password unchanged\n");
1846		else {
1847		    authusekey(info_auth_keyid, info_auth_keytype,
1848			       (u_char *)pass);
1849		    authtrust(info_auth_keyid, 1);
1850		}
1851	}
1852}
1853
1854
1855/*
1856 * hostnames - set the showhostnames flag
1857 */
1858static void
1859hostnames(
1860	struct parse *pcmd,
1861	FILE *fp
1862	)
1863{
1864	if (pcmd->nargs == 0) {
1865		if (showhostnames)
1866		    (void) fprintf(fp, "hostnames being shown\n");
1867		else
1868		    (void) fprintf(fp, "hostnames not being shown\n");
1869	} else {
1870		if (STREQ(pcmd->argval[0].string, "yes"))
1871		    showhostnames = 1;
1872		else if (STREQ(pcmd->argval[0].string, "no"))
1873		    showhostnames = 0;
1874		else
1875		    (void)fprintf(stderr, "What?\n");
1876	}
1877}
1878
1879
1880/*
1881 * setdebug - set/change debugging level
1882 */
1883static void
1884setdebug(
1885	struct parse *pcmd,
1886	FILE *fp
1887	)
1888{
1889	if (pcmd->nargs == 0) {
1890		(void) fprintf(fp, "debug level is %d\n", debug);
1891		return;
1892	} else if (STREQ(pcmd->argval[0].string, "no")) {
1893		debug = 0;
1894	} else if (STREQ(pcmd->argval[0].string, "more")) {
1895		debug++;
1896	} else if (STREQ(pcmd->argval[0].string, "less")) {
1897		debug--;
1898	} else {
1899		(void) fprintf(fp, "What?\n");
1900		return;
1901	}
1902	(void) fprintf(fp, "debug level set to %d\n", debug);
1903}
1904
1905
1906/*
1907 * quit - stop this nonsense
1908 */
1909/*ARGSUSED*/
1910static void
1911quit(
1912	struct parse *pcmd,
1913	FILE *fp
1914	)
1915{
1916	if (havehost)
1917	    closesocket(sockfd);
1918	exit(0);
1919}
1920
1921
1922/*
1923 * version - print the current version number
1924 */
1925/*ARGSUSED*/
1926static void
1927version(
1928	struct parse *pcmd,
1929	FILE *fp
1930	)
1931{
1932
1933	(void) fprintf(fp, "%s\n", Version);
1934	return;
1935}
1936
1937
1938/*
1939 * warning - print a warning message
1940 */
1941static void
1942warning(
1943	const char *fmt,
1944	const char *st1,
1945	const char *st2
1946	)
1947{
1948	(void) fprintf(stderr, "%s: ", progname);
1949	(void) fprintf(stderr, fmt, st1, st2);
1950	(void) fprintf(stderr, ": ");
1951	perror("");
1952}
1953
1954
1955/*
1956 * error - print a message and exit
1957 */
1958static void
1959error(
1960	const char *fmt,
1961	const char *st1,
1962	const char *st2
1963	)
1964{
1965	warning(fmt, st1, st2);
1966	exit(1);
1967}
1968
1969/*
1970 * getkeyid - prompt the user for a keyid to use
1971 */
1972static u_long
1973getkeyid(
1974	const char *keyprompt
1975	)
1976{
1977	register char *p;
1978	register int c;
1979	FILE *fi;
1980	char pbuf[20];
1981
1982#ifndef SYS_WINNT
1983	if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
1984#else
1985	    if ((fi = _fdopen((int)GetStdHandle(STD_INPUT_HANDLE), "r")) == NULL)
1986#endif /* SYS_WINNT */
1987		fi = stdin;
1988	    else
1989		setbuf(fi, (char *)NULL);
1990	fprintf(stderr, "%s", keyprompt); fflush(stderr);
1991	for (p=pbuf; (c = getc(fi))!='\n' && c!=EOF;) {
1992		if (p < &pbuf[18])
1993		    *p++ = (char) c;
1994	}
1995	*p = '\0';
1996	if (fi != stdin)
1997	    fclose(fi);
1998	return (u_int32)atoi(pbuf);
1999}