ntpdc.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* xntpdc - control and monitor your xntpd daemon
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <setjmp.h>
#include <netdb.h>
#ifdef SYS_WINNT
#include <io.h>
#else
#define closesocket close
#endif /* SYS_WINNT */
#include "ntpdc.h"
#include "ntp_select.h"
#include "ntp_io.h"
#include "ntp_stdlib.h"
#ifdef SYS_VXWORKS
/* vxWorks needs mode flag -casey*/
#define SERVER_PORT_NUM 123
#endif
#if defined(VMS)
extern char *getpass(const char *);
#endif
/*
* Because we now potentially understand a lot of commands (and
* it requires a lot of commands to talk to xntpd) we will run
* interactive if connected to a terminal.
*/
static int interactive = 0; /* set to 1 when we should prompt */
/*
* Keyid used for authenticated requests. Obtained on the fly.
*/
static u_int32 info_auth_keyid;
/*
* Type of key md5 or des
*/
#define KEY_TYPE_DES 3
#define KEY_TYPE_MD5 4
/*
* Built in command handler declarations
*/
static int openhost P((char *));
static int sendpkt P((char *, int));
static void growpktdata P((void));
static int getresponse P((int, int, int *, int *, char **));
static int sendrequest P((int, int, int, int, int, char *));
static void getcmds P((void));
static RETSIGTYPE abortcmd P((int));
static void docmd P((char *));
static void tokenize P((char *, char **, int *));
#ifdef QSORT_USES_VOID_P
static int helpsort P((const void *, const void *));
#else
static int helpsort P((char **, char **));
#endif
static void warning P((char *, char *, char *));
static void error P((char *, char *, char *));
/*
* Built-in commands we understand
*/
{ "command", "", "", "" },
"tell the use and syntax of commands" },
{ "command", "", "", "" },
"tell the use and syntax of commands" },
{ "msec", "", "", "" },
"set the primary receive time out" },
{ "msec", "", "", "" },
"set the delay added to encryption time stamps" },
{ "hostname", "", "", "" },
"specify the host whose NTP server we talk to" },
{ "", "", "", "" },
"specify a password to use for authenticated requests"},
{ "yes|no", "", "", "" },
"specify whether hostnames or net numbers are printed"},
{ "no|more|less", "", "", "" },
{ "", "", "", "" },
"exit xntpdc" },
{ "", "", "", "" },
"exit xntpdc" },
{ "key#", "", "", "" },
{ "(md5|des)", "", "", "" },
{ "", "", "", "" },
"print version number" },
{ "", "", "", "" }, "" }
};
/*
* Default values we use.
*/
/*
* Some variables used and manipulated locally
*/
static int sockfd; /* fd socket is openned on */
static int havehost = 0; /* set to 1 when host open */
#if defined (SYS_WINNT) || defined (SYS_VXWORKS)
char password[9];
#endif /* SYS_WINNT || SYS_VXWORKS */
#ifdef SYS_WINNT
#endif /* SYS_WINNT */
/*
* Holds data returned from queries. We allocate INITDATASIZE
* octets to begin with, increasing this as we need to.
*/
static char *pktdata;
static int pktdatasize;
/*
* For commands typed on the command line (with the -c option)
*/
static int numcmds = 0;
/*
* When multiple hosts are specified.
*/
static int numhosts = 0;
/*
* Error codes for internal use
*/
#define ERR_INCOMPLETE 16
#define ERR_TIMEOUT 17
/*
* Macro definitions we use
*/
/*
* For converting time stamps to dates
*/
/*
* Jump buffer for longjumping back to the command level
*/
static jmp_buf interrupt_buf;
static volatile int jump = 0;
/*
* Pointer to current output unit
*/
static FILE *current_output;
/*
* Command table imported from ntpdc_ops.c
*/
char *progname;
int debug;
#ifdef NO_MAIN_ALLOWED
void xntpdcmain P((int, char *[]));
#else
int main P((int, char *[]));
#endif
#ifdef SYS_VXWORKS
void clear_globals()
{
extern int ntp_optind;
extern char *ntp_optarg;
showhostnames = 0; /* show host names by default */
ntp_optind = 0;
ntp_optarg = 0;
havehost = 0; /* set to 1 when host open */
numcmds = 0;
numhosts = 0;
}
#endif
/*
* main - parse arguments and handle options
*/
#ifdef NO_MAIN_ALLOWED
void xntpdcmain
#else
int main
#endif
int argc;
char *argv[];
{
int c;
int errflg = 0;
extern int ntp_optind;
extern char *ntp_optarg;
delay_time.l_ui = 0;
#ifdef SYS_VXWORKS
#endif
switch (c) {
case 'c':
break;
case 'd':
++debug;
break;
case 'i':
interactive = 1;
break;
case 'l':
ADDCMD("listpeers");
break;
case 'n':
showhostnames = 0;
break;
case 'p':
ADDCMD("peers");
break;
case 's':
ADDCMD("dmpeers");
break;
default:
errflg++;
break;
}
if (errflg) {
"usage: %s [-dilnps] [-c cmd] host ...\n",
progname);
exit(2);
}
if (ntp_optind == argc) {
} else {
}
if (numcmds == 0 && interactive == 0
interactive = 1;
}
#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
if (interactive)
#endif /* SYS_WINNT */
/*
* Initialize the packet data buffer
*/
exit(1);
}
#ifdef SYS_WINNT
exit(1);
}
#endif /* SYS_WINNT */
if (numcmds == 0) {
getcmds();
} else {
int ihost;
int icmd;
if (numhosts > 1)
}
}
}
#ifdef SYS_WINNT
WSACleanup();
#endif
#ifndef SYS_VXWORKS
exit(0);
#endif
} /* main end */
/*
* openhost - open a socket to a host
*/
static int
char *hname;
{
char temphost[LENHOSTNAME];
if (server_entry == NULL) {
if (server_entry == NULL) {
#ifdef VMS /* UCX getservbyname() doesn't work [yet], but we do know better */
server_entry = (struct servent *)
#else
progname);
exit(1);
#endif /* VMS & UCX */
}
if (debug > 2)
}
return 0;
if (debug > 2)
if (havehost == 1) {
if (debug > 2)
(void) closesocket(sockfd);
havehost = 0;
}
#ifndef SYS_VXWORKS
#else
#endif
#ifdef SYS_WINNT
{
int err;
err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&optionValue, sizeof(optionValue));
exit(1);
}
}
if (sockfd == INVALID_SOCKET) {
exit(-1);
}
#else
if (sockfd == -1)
#endif /* SYS_WINNT */
#ifdef NEED_RCVBUF_SLOP
# ifdef SO_RCVBUF
{
&rbufsize, sizeof(int)) == -1)
}
# endif
#endif
sizeof(hostaddr)) == -1)
havehost = 1;
return 1;
}
/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
/*
* sendpkt - send a packet to the remote host
*/
static int
char *xdata;
int xdatalen;
{
return -1;
}
return 0;
}
/*
* growpktdata - grow the packet data area
*/
static void
{
if (pktdata == 0) {
exit(1);
}
}
/*
* getresponse - get a (series of) response packet(s) and return the data
*/
static int
int implcode;
int reqcode;
int *ritems;
int *rsize;
char **rdata;
{
int items;
int size;
int datasize;
char *datap;
int firstpkt;
int lastseq;
int numrecv;
int seq;
int n;
/*
* This is pretty tricky. We may get between 1 and many packets
* back in response to the request. We peel the data out of
* each packet and collect it in one long block. When the last
* packet in the sequence is received we'll know how many we
* should have had. Note we use one long time out, should reconsider.
*/
*ritems = 0;
*rsize = 0;
firstpkt = 1;
numrecv = 0;
if (firstpkt)
else
if (n == -1) {
return -1;
}
if (n == 0) {
/*
* Timed out. Return what we have
*/
if (firstpkt) {
"%s: timed out, nothing received\n", currenthost);
return ERR_TIMEOUT;
} else {
"%s: timed out with incomplete data\n",
if (debug) {
printf("Received sequence numbers");
for (n = 0; n <= MAXSEQ; n++)
if (haveseq[n])
printf(" %d,", n);
if (lastseq != 999)
printf(" last frame received\n");
else
printf(" last frame not received\n");
}
return ERR_INCOMPLETE;
}
}
if (n == -1) {
return -1;
}
/*
* Check for format errors. Bug proofing.
*/
if (n < RESP_HEADER_SIZE) {
if (debug)
printf("Short (%d byte) packet received\n", n);
goto again;
}
if (debug)
printf("Packet received with version %d\n",
goto again;
}
if (debug)
printf("Packet received with mode %d\n",
goto again;
}
if (debug)
printf("Encrypted packet received\n");
goto again;
}
if (debug)
printf("Received request packet, wanted response\n");
goto again;
}
if (debug)
printf("Received packet with nonzero MBZ field!\n");
goto again;
}
/*
* Check implementation/request. Could be old data getting to us.
*/
if (debug)
"Received implementation/request of %d/%d, wanted %d/%d",
goto again;
}
/*
* Check the error code. If non-zero, return it.
*/
printf("Error code %d received on not-final packet\n",
}
}
/*
* Collect items and size. Make sure they make sense.
*/
if (debug)
"Received items %d, size %d (total %d), data in packet is %d\n",
goto again;
}
/*
* If this isn't our first packet, make sure the size matches
* the other ones.
*/
if (debug)
printf("Received itemsize %d, previous %d\n",
goto again;
}
/*
* If we've received this before, toss it
*/
if (debug)
goto again;
}
/*
* If this is the last in the sequence, record that.
*/
if (lastseq != 999) {
printf("Received second end sequence packet\n");
goto again;
}
}
/*
* So far, so good. Copy this data into the output array.
*/
growpktdata();
}
if (firstpkt) {
firstpkt = 0;
}
/*
* Finally, check the count of received packets. If we've got them
* all, return
*/
++numrecv;
goto again;
return INFO_OKAY;
}
/*
* sendrequest - format and send a request packet
*/
static int
int implcode;
int reqcode;
int auth;
int qitems;
int qsize;
char *qdata;
{
int datasize;
} else {
}
if (!auth) {
} else {
char *pass;
if (info_auth_keyid == 0) {
if (info_auth_keyid == 0) {
"Keyid must be defined, request not sent\n");
return 1;
}
}
if (!auth_havekey(info_auth_keyid)) {
? "DES Password: "
: "MD5 Password: "
);
if (*pass != '\0')
pass);
}
if (auth_havekey(info_auth_keyid)) {
int maclen;
get_systime(&ts);
} else {
"No password, request not sent\n");
return 1;
}
}
/*NOTREACHED*/
}
/*
* doquery - send a request and process the response
*/
int
int implcode;
int reqcode;
int auth;
int qitems;
int qsize;
char *qdata;
int *ritems;
int *rsize;
char **rdata;
int quiet_mask;
{
int res;
char junk[512];
/*
* Check to make sure host is open
*/
if (!havehost) {
return -1;
}
/*
* Poll the socket and clear out any pending data
*/
do {
if (res == -1) {
return -1;
} else if (res > 0)
} while (res > 0);
/*
* send a request
*/
if (res != 0)
return res;
/*
* Get the response. If we got a standard error, print a message
*/
/* log error message if not told to be quiet */
switch(res) {
case INFO_ERR_IMPL:
"***Server implementation incompatable with our own\n");
break;
case INFO_ERR_REQ:
"***Server doesn't implement this request\n");
break;
case INFO_ERR_FMT:
"***Server reports a format error in the received packet (shouldn't happen)\n");
break;
case INFO_ERR_NODATA:
"***Server reports data not found\n");
break;
case INFO_ERR_AUTH:
break;
case ERR_TIMEOUT:
break;
case ERR_INCOMPLETE:
"***Response from server was incomplete\n");
break;
default:
"***Server returns unknown error code %d\n", res);
break;
}
}
return res;
}
/*
* getcmds - read commands from the standard input and execute them
*/
static void
getcmds()
{
for (;;) {
if (interactive) {
#ifdef VMS /* work around a problem with mixing stdout & stderr */
#endif
}
return;
}
}
/*
* abortcmd - catch interrupts and abort the current command
*/
static RETSIGTYPE
int sig;
{
if (current_output == stdout)
}
/*
* docmd - decode the command line and execute a command
*/
static void
char *cmdline;
{
int ntok;
static int i;
/*
* Tokenize the command line. If nothing on it, return.
*/
if (ntok == 0)
return;
/*
* Find the appropriate command description.
*/
if (i == 0) {
tokens[0]);
return;
} else if (i >= 2) {
tokens[0]);
return;
}
/*
* Save the keyword, then walk through the arguments, interpreting
* as we go.
*/
if ((i+1) >= ntok) {
return;
}
break;
}
break;
return;
}
i++;
char *fname;
else if ((i+1) < ntok)
else {
return;
}
if (current_output == NULL) {
perror("");
return;
}
i = 1; /* flag we need a close */
} else {
i = 0; /* flag no close */
}
return;
} else {
jump = 1;
jump = 0;
if (i) (void) fclose(current_output);
}
}
/*
* tokenize - turn a command line into tokens
*/
static void
char *line;
char **tokens;
int *ntok;
{
register char *cp;
register char *sp;
cp++;
break;
do {
*sp++ = '\0';
}
}
/*
* findcmd - find a command in a command description table
*/
static int
register char *str;
{
register int clen;
int nmatch;
nmatch = 0;
if (clist1 != 0)
else if (clist2 != 0)
else
return 0;
/* do a first character check, for efficiency */
continue;
/*
* Could be extact match, could be approximate.
* Is exact if the length of the keyword is the
* same as the str.
*/
return 1;
}
nmatch++;
}
}
/*
* See if there is more to do. If so, go again. Sorry about the
* goto, too much looking at BSD sources...
*/
goto again;
}
/*
* If we got extactly 1 near match, use it, else return number
* of matches.
*/
if (nmatch == 1) {
return 1;
}
return nmatch;
}
/*
* getarg - interpret an argument token
*/
static int
char *str;
int code;
{
int isneg;
static const char *digits = "0123456789";
case NTP_STR:
break;
case ADD:
return 0;
}
break;
case INT:
case UINT:
isneg = 0;
if (*np == '-') {
np++;
isneg = 1;
}
do {
"***Illegal integer value %s\n", str);
return 0;
}
} while (*(++np) != '\0');
if (isneg) {
"***Value %s should be unsigned\n", str);
return 0;
}
}
break;
}
return 1;
}
/*
* getnetnum - given a host name, return its net number
* and (optional) full name
*/
static int
char *host;
char *fullhost;
{
if (fullhost != 0) {
}
return 1;
if (fullhost != 0)
return 1;
} else {
return 0;
}
/*NOTREACHED*/
}
/*
* nntohost - convert network number to host name. This routine enforces
* the showhostnames setting.
*/
char *
{
if (!showhostnames)
}
/*
* Finally, the built in command handlers
*/
/*
* help - tell about commands, or details of a particular command
*/
static void
{
int i;
int n;
char *cmd;
const char *cmdsort[100];
int length[100];
int maxlength;
int numperline;
n = 0;
}
#ifdef QSORT_USES_VOID_P
#else
#endif
maxlength = 0;
for (i = 0; i < n; i++) {
}
maxlength++;
for (i = 0; i < n; i++) {
|| i == (n-1))
else
}
} else {
if (n == 0) {
"Command `%s' is unknown\n", cmd);
return;
} else if (n >= 2) {
"Command `%s' is ambiguous\n", cmd);
return;
}
}
}
/*
* helpsort - do hostname qsort comparisons
*/
static int
#ifdef QSORT_USES_VOID_P
const void *t1;
const void *t2;
{
#else
char **name1;
char **name2;
{
#endif /* sgi || bsdi */
}
/*
* printusage - print usage information for a command
*/
static void
{
register int i;
else
}
}
/*
* timeout - set time out time
*/
static void
{
int val;
} else {
* 1000;
}
}
/*
* delay - set delay for auth requests
*/
static void
{
int isneg;
} else {
isneg = 1;
} else {
isneg = 0;
}
val %= 1000;
if (isneg)
L_NEG(&delay_time);
}
}
/*
* host - set the host we are dealing with.
*/
static void
{
if (havehost)
else
} else {
if (havehost)
"current host remains %s\n", currenthost);
else
}
}
/*
* keyid - get a keyid to use for authenticating requests
*/
static void
{
if (info_auth_keyid == 0)
else
} else {
}
}
/*
* keytype - get type of key to use for authenticating requests
*/
static void
{
else
case 'm':
case 'M':
break;
case 'd':
case 'D':
break;
default:
}
}
/*
* passwd - get an authentication key
*/
/*ARGSUSED*/
static void
{
char *pass;
if (info_auth_keyid == 0) {
if (info_auth_keyid == 0) {
return;
}
}
if (!interactive) {
} else {
? "DES Password: "
: "MD5 Password: "
);
if (*pass == '\0')
else
}
}
/*
* hostnames - set the showhostnames flag
*/
static void
{
if (showhostnames)
else
} else {
showhostnames = 1;
showhostnames = 0;
else
}
}
/*
*/
static void
{
return;
debug = 0;
debug++;
debug--;
} else {
return;
}
}
/*
* quit - stop this nonsense
*/
/*ARGSUSED*/
static void
{
if (havehost)
exit(0);
}
/*
* version - print the current version number
*/
/*ARGSUSED*/
static void
{
extern char *Version;
}
/*
* warning - print a warning message
*/
static void
char *fmt;
char *st1;
char *st2;
{
perror("");
}
/*
* error - print a message and exit
*/
static void
char *fmt;
char *st1;
char *st2;
{
exit(1);
}
/*
* getkeyid - prompt the user for a keyid to use
*/
static u_int32
char *prompt;
{
register char *p;
register int c;
char pbuf[20];
#ifndef SYS_WINNT
#else
#endif /* SYS_WINNT */
else
if (p < &pbuf[18])
*p++ = c;
}
*p = '\0';
}