diff options
author | delphij <delphij@FreeBSD.org> | 2015-07-15 19:21:26 +0000 |
---|---|---|
committer | delphij <delphij@FreeBSD.org> | 2015-07-15 19:21:26 +0000 |
commit | 2a25cee78ab1d37e7d2bc40ae675646974d99f56 (patch) | |
tree | b0302ac4be59e104f4e1e54014561a1389397192 /contrib/ntp/ntpq/ntpq.c | |
parent | a0741a75537b2e0514472ac3b28afc55a7846c30 (diff) | |
download | FreeBSD-src-2a25cee78ab1d37e7d2bc40ae675646974d99f56.zip FreeBSD-src-2a25cee78ab1d37e7d2bc40ae675646974d99f56.tar.gz |
MFC r280849,280915-280916,281015-281016,282097,282408,282415,283542,
284864,285169-285170,285435:
ntp 4.2.8p3.
Relnotes: yes
Approved by: re (?)
Diffstat (limited to 'contrib/ntp/ntpq/ntpq.c')
-rw-r--r-- | contrib/ntp/ntpq/ntpq.c | 3061 |
1 files changed, 1666 insertions, 1395 deletions
diff --git a/contrib/ntp/ntpq/ntpq.c b/contrib/ntp/ntpq/ntpq.c index 2e51b35..af5f681 100644 --- a/contrib/ntp/ntpq/ntpq.c +++ b/contrib/ntp/ntpq/ntpq.c @@ -1,42 +1,46 @@ /* * ntpq - query an NTP server using mode 6 commands */ - +#include <config.h> #include <stdio.h> - #include <ctype.h> #include <signal.h> #include <setjmp.h> #include <sys/types.h> #include <sys/time.h> +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif +#ifdef HAVE_FCNTL_H +# include <fcntl.h> +#endif +#ifdef SYS_WINNT +# include <mswsock.h> +#endif +#include <isc/net.h> +#include <isc/result.h> #include "ntpq.h" +#include "ntp_assert.h" +#include "ntp_stdlib.h" #include "ntp_unixtime.h" #include "ntp_calendar.h" -#include "ntp_io.h" #include "ntp_select.h" -#include "ntp_stdlib.h" -/* Don't include ISC's version of IPv6 variables and structures */ -#define ISC_IPV6_H 1 -#include "isc/net.h" -#include "isc/result.h" +#include "ntp_assert.h" +#include "lib_strbuf.h" +#include "ntp_lineedit.h" +#include "ntp_debug.h" +#ifdef OPENSSL +#include "openssl/evp.h" +#include "openssl/objects.h" +#include "openssl/err.h" +#endif +#include <ssl_applink.c> +#include "ntp_libopts.h" #include "ntpq-opts.h" -#ifdef SYS_WINNT -# include <Mswsock.h> -# include <io.h> -#else -# define closesocket close -#endif /* SYS_WINNT */ - -#if defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDIT) -# include <readline/readline.h> -# include <readline/history.h> -#endif /* HAVE_LIBREADLINE || HAVE_LIBEDIT */ - -#ifdef SYS_VXWORKS - /* vxWorks needs mode flag -casey*/ +#ifdef SYS_VXWORKS /* vxWorks needs mode flag -casey*/ # define open(name, flags) open(name, flags, 0777) # define SERVER_PORT_NUM 123 #endif @@ -53,6 +57,15 @@ int interactive = 0; /* set to 1 when we should prompt */ const char *prompt = "ntpq> "; /* prompt to ask him about */ +/* + * use old readvars behavior? --old-rv processing in ntpq resets + * this value based on the presence or absence of --old-rv. It is + * initialized to 1 here to maintain backward compatibility with + * libntpq clients such as ntpsnmpd, which are free to reset it as + * desired. + */ +int old_rv = 1; + /* * for get_systime() @@ -64,12 +77,8 @@ s_char sys_precision; /* local clock precision (log2 s) */ */ u_long info_auth_keyid = 0; -/* - * Type of key md5 - */ -#define KEY_TYPE_MD5 4 - -static int info_auth_keytype = KEY_TYPE_MD5; /* MD5 */ +static int info_auth_keytype = NID_md5; /* MD5 */ +static size_t info_auth_hashlen = 16; /* MD5 */ u_long current_time; /* needed by authkeys; not used */ /* @@ -96,127 +105,41 @@ volatile int jump = 0; * Format values */ #define PADDING 0 -#define TS 1 /* time stamp */ -#define FL 2 /* l_fp type value */ -#define FU 3 /* u_fp type value */ -#define FS 4 /* s_fp type value */ -#define UI 5 /* unsigned integer value */ -#define SI 6 /* signed integer value */ -#define HA 7 /* host address */ -#define NA 8 /* network address */ -#define ST 9 /* string value */ -#define RF 10 /* refid (sometimes string, sometimes not) */ -#define LP 11 /* leap (print in binary) */ -#define OC 12 /* integer, print in octal */ -#define MD 13 /* mode */ -#define AR 14 /* array of times */ -#define FX 15 /* test flags */ +#define HA 1 /* host address */ +#define NA 2 /* network address */ +#define LP 3 /* leap (print in binary) */ +#define RF 4 /* refid (sometimes string, sometimes not) */ +#define AR 5 /* array of times */ +#define FX 6 /* test flags */ +#define TS 7 /* l_fp timestamp in hex */ +#define OC 8 /* integer, print in octal */ #define EOV 255 /* end of table */ - -/* - * System variable values. The array can be indexed by - * the variable index to find the textual name. - */ -struct ctl_var sys_var[] = { - { 0, PADDING, "" }, /* 0 */ - { CS_LEAP, LP, "leap" }, /* 1 */ - { CS_STRATUM, UI, "stratum" }, /* 2 */ - { CS_PRECISION, SI, "precision" }, /* 3 */ - { CS_ROOTDELAY, FS, "rootdelay" }, /* 4 */ - { CS_ROOTDISPERSION, FU, "rootdispersion" }, /* 5 */ - { CS_REFID, RF, "refid" }, /* 6 */ - { CS_REFTIME, TS, "reftime" }, /* 7 */ - { CS_POLL, UI, "poll" }, /* 8 */ - { CS_PEERID, UI, "peer" }, /* 9 */ - { CS_STATE, UI, "state" }, /* 10 */ - { CS_OFFSET, FL, "offset" }, /* 11 */ - { CS_DRIFT, FS, "frequency" }, /* 12 */ - { CS_JITTER, FU, "jitter" }, /* 13 */ - { CS_CLOCK, TS, "clock" }, /* 14 */ - { CS_PROCESSOR, ST, "processor" }, /* 15 */ - { CS_SYSTEM, ST, "system" }, /* 16 */ - { CS_VERSION, ST, "version" }, /* 17 */ - { CS_STABIL, FS, "stability" }, /* 18 */ - { CS_VARLIST, ST, "sys_var_list" }, /* 19 */ - { 0, EOV, "" } -}; - - /* - * Peer variable list - */ -struct ctl_var peer_var[] = { - { 0, PADDING, "" }, /* 0 */ - { CP_CONFIG, UI, "config" }, /* 1 */ - { CP_AUTHENABLE, UI, "authenable" }, /* 2 */ - { CP_AUTHENTIC, UI, "authentic" }, /* 3 */ - { CP_SRCADR, HA, "srcadr" }, /* 4 */ - { CP_SRCPORT, UI, "srcport" }, /* 5 */ - { CP_DSTADR, NA, "dstadr" }, /* 6 */ - { CP_DSTPORT, UI, "dstport" }, /* 7 */ - { CP_LEAP, LP, "leap" }, /* 8 */ - { CP_HMODE, MD, "hmode" }, /* 9 */ - { CP_STRATUM, UI, "stratum" }, /* 10 */ - { CP_PPOLL, UI, "ppoll" }, /* 11 */ - { CP_HPOLL, UI, "hpoll" }, /* 12 */ - { CP_PRECISION, SI, "precision" }, /* 13 */ - { CP_ROOTDELAY, FS, "rootdelay" }, /* 14 */ - { CP_ROOTDISPERSION, FU, "rootdispersion" }, /* 15 */ - { CP_REFID, RF, "refid" }, /* 16 */ - { CP_REFTIME, TS, "reftime" }, /* 17 */ - { CP_ORG, TS, "org" }, /* 18 */ - { CP_REC, TS, "rec" }, /* 19 */ - { CP_XMT, TS, "xmt" }, /* 20 */ - { CP_REACH, OC, "reach" }, /* 21 */ - { CP_UNREACH, UI, "unreach" }, /* 22 */ - { CP_TIMER, UI, "timer" }, /* 23 */ - { CP_DELAY, FS, "delay" }, /* 24 */ - { CP_OFFSET, FL, "offset" }, /* 25 */ - { CP_JITTER, FU, "jitter" }, /* 26 */ - { CP_DISPERSION, FU, "dispersion" }, /* 27 */ - { CP_KEYID, UI, "keyid" }, /* 28 */ - { CP_FILTDELAY, AR, "filtdelay" }, /* 29 */ - { CP_FILTOFFSET, AR, "filtoffset" }, /* 30 */ - { CP_PMODE, ST, "pmode" }, /* 31 */ - { CP_RECEIVED, UI, "received" }, /* 32 */ - { CP_SENT, UI, "sent" }, /* 33 */ - { CP_FILTERROR, AR, "filtdisp" }, /* 34 */ - { CP_FLASH, FX, "flash" }, /* 35 */ - { CP_TTL, UI, "ttl" }, /* 36 */ - /* - * These are duplicate entries so that we can - * process deviant version of the ntp protocol. - */ - { CP_SRCADR, HA, "peeraddr" }, /* 4 */ - { CP_SRCPORT, UI, "peerport" }, /* 5 */ - { CP_PPOLL, UI, "peerpoll" }, /* 11 */ - { CP_HPOLL, UI, "hostpoll" }, /* 12 */ - { CP_FILTERROR, AR, "filterror" }, /* 34 */ - { 0, EOV, "" } + * For the most part ntpq simply displays what ntpd provides in the + * mostly plain-text mode 6 responses. A few variable names are by + * default "cooked" to provide more human-friendly output. + */ +const var_format cookedvars[] = { + { "leap", LP }, + { "reach", OC }, + { "refid", RF }, + { "reftime", TS }, + { "clock", TS }, + { "org", TS }, + { "rec", TS }, + { "xmt", TS }, + { "flash", FX }, + { "srcadr", HA }, + { "peeradr", HA }, /* compat with others */ + { "dstadr", NA }, + { "filtdelay", AR }, + { "filtoffset", AR }, + { "filtdisp", AR }, + { "filterror", AR }, /* compat with others */ }; -/* - * Clock variable list - */ -struct ctl_var clock_var[] = { - { 0, PADDING, "" }, /* 0 */ - { CC_TYPE, UI, "type" }, /* 1 */ - { CC_TIMECODE, ST, "timecode" }, /* 2 */ - { CC_POLL, UI, "poll" }, /* 3 */ - { CC_NOREPLY, UI, "noreply" }, /* 4 */ - { CC_BADFORMAT, UI, "badformat" }, /* 5 */ - { CC_BADDATA, UI, "baddata" }, /* 6 */ - { CC_FUDGETIME1, FL, "fudgetime1" }, /* 7 */ - { CC_FUDGETIME2, FL, "fudgetime2" }, /* 8 */ - { CC_FUDGEVAL1, UI, "stratum" }, /* 9 */ - { CC_FUDGEVAL2, RF, "refid" }, /* 10 */ - { CC_FLAGS, UI, "flags" }, /* 11 */ - { CC_DEVICE, ST, "device" }, /* 12 */ - { 0, EOV, "" } -}; - /* * flasher bits @@ -224,76 +147,85 @@ struct ctl_var clock_var[] = { static const char *tstflagnames[] = { "pkt_dup", /* TEST1 */ "pkt_bogus", /* TEST2 */ - "pkt_proto", /* TEST3 */ + "pkt_unsync", /* TEST3 */ "pkt_denied", /* TEST4 */ "pkt_auth", /* TEST5 */ - "pkt_synch", /* TEST6 */ - "pkt_dist", /* TEST7 */ + "pkt_stratum", /* TEST6 */ + "pkt_header", /* TEST7 */ "pkt_autokey", /* TEST8 */ "pkt_crypto", /* TEST9 */ "peer_stratum", /* TEST10 */ "peer_dist", /* TEST11 */ "peer_loop", /* TEST12 */ - "peer_unfit" /* TEST13 */ + "peer_unreach" /* TEST13 */ }; -int ntpqmain P((int, char **)); +int ntpqmain (int, char **); /* * Built in command handler declarations */ -static int openhost P((const char *)); -static int sendpkt P((char *, int)); -static int getresponse P((int, int, u_short *, int *, char **, int)); -static int sendrequest P((int, int, int, int, char *)); -static char * tstflags P((u_long)); -static void getcmds P((void)); -static RETSIGTYPE abortcmd P((int)); -static void docmd P((const char *)); -static void tokenize P((const char *, char **, int *)); -static int findcmd P((char *, struct xcmd *, struct xcmd *, struct xcmd **)); -static int getarg P((char *, int, arg_v *)); -static int rtdatetolfp P((char *, l_fp *)); -static int decodearr P((char *, int *, l_fp *)); -static void help P((struct parse *, FILE *)); -#ifdef QSORT_USES_VOID_P -static int helpsort P((const void *, const void *)); -#else -static int helpsort P((char **, char **)); +static int openhost (const char *, int); +static void dump_hex_printable(const void *, size_t); +static int sendpkt (void *, size_t); +static int getresponse (int, int, u_short *, int *, const char **, int); +static int sendrequest (int, associd_t, int, int, const char *); +static char * tstflags (u_long); +#ifndef BUILD_AS_LIB +static void getcmds (void); +#ifndef SYS_WINNT +static RETSIGTYPE abortcmd (int); +#endif /* SYS_WINNT */ +static void docmd (const char *); +static void tokenize (const char *, char **, int *); +static int getarg (const char *, int, arg_v *); +#endif /* BUILD_AS_LIB */ +static int findcmd (const char *, struct xcmd *, + struct xcmd *, struct xcmd **); +static int rtdatetolfp (char *, l_fp *); +static int decodearr (char *, int *, l_fp *); +static void help (struct parse *, FILE *); +static int helpsort (const void *, const void *); +static void printusage (struct xcmd *, FILE *); +static void timeout (struct parse *, FILE *); +static void auth_delay (struct parse *, FILE *); +static void host (struct parse *, FILE *); +static void ntp_poll (struct parse *, FILE *); +static void keyid (struct parse *, FILE *); +static void keytype (struct parse *, FILE *); +static void passwd (struct parse *, FILE *); +static void hostnames (struct parse *, FILE *); +static void setdebug (struct parse *, FILE *); +static void quit (struct parse *, FILE *); +static void version (struct parse *, FILE *); +static void raw (struct parse *, FILE *); +static void cooked (struct parse *, FILE *); +static void authenticate (struct parse *, FILE *); +static void ntpversion (struct parse *, FILE *); +static void warning (const char *, ...) + __attribute__((__format__(__printf__, 1, 2))); +static void error (const char *, ...) + __attribute__((__format__(__printf__, 1, 2))); +static u_long getkeyid (const char *); +static void atoascii (const char *, size_t, char *, size_t); +static void cookedprint (int, int, const char *, int, int, FILE *); +static void rawprint (int, int, const char *, int, int, FILE *); +static void startoutput (void); +static void output (FILE *, const char *, const char *); +static void endoutput (FILE *); +static void outputarr (FILE *, char *, int, l_fp *); +static int assoccmp (const void *, const void *); + u_short varfmt (const char *); + +void ntpq_custom_opt_handler (tOptions *, tOptDesc *); + +#ifdef OPENSSL +# ifdef HAVE_EVP_MD_DO_ALL_SORTED +static void list_md_fn(const EVP_MD *m, const char *from, + const char *to, void *arg ); +# endif #endif -static void printusage P((struct xcmd *, FILE *)); -static void timeout P((struct parse *, FILE *)); -static void auth_delay P((struct parse *, FILE *)); -static void host P((struct parse *, FILE *)); -static void ntp_poll P((struct parse *, FILE *)); -static void keyid P((struct parse *, FILE *)); -static void keytype P((struct parse *, FILE *)); -static void passwd P((struct parse *, FILE *)); -static void hostnames P((struct parse *, FILE *)); -static void setdebug P((struct parse *, FILE *)); -static void quit P((struct parse *, FILE *)); -static void version P((struct parse *, FILE *)); -static void raw P((struct parse *, FILE *)); -static void cooked P((struct parse *, FILE *)); -static void authenticate P((struct parse *, FILE *)); -static void ntpversion P((struct parse *, FILE *)); -static void warning P((const char *, const char *, const char *)); -static void error P((const char *, const char *, const char *)); -static u_long getkeyid P((const char *)); -static void atoascii P((int, char *, char *)); -static void makeascii P((int, char *, FILE *)); -static void rawprint P((int, int, char *, int, FILE *)); -static void startoutput P((void)); -static void output P((FILE *, char *, char *)); -static void endoutput P((FILE *)); -static void outputarr P((FILE *, char *, int, l_fp *)); -static void cookedprint P((int, int, char *, int, FILE *)); -#ifdef QSORT_USES_VOID_P -static int assoccmp P((const void *, const void *)); -#else -static int assoccmp P((struct association *, struct association *)); -#endif /* sgi || bsdi */ - +static char *list_digest_names(void); /* * Built-in commands we understand @@ -317,7 +249,7 @@ struct xcmd builtins[] = { { "poll", ntp_poll, { OPT|NTP_UINT, OPT|NTP_STR, NO, NO }, { "n", "verbose", "", "" }, "poll an NTP server in client mode `n' times" }, - { "passwd", passwd, { NO, NO, NO, NO }, + { "passwd", passwd, { OPT|NTP_STR, NO, NO, NO }, { "", "", "", "" }, "specify a password to use for authenticated requests"}, { "hostnames", hostnames, { OPT|NTP_STR, NO, NO, NO }, @@ -351,8 +283,8 @@ struct xcmd builtins[] = { { "version number", "", "", "" }, "set the NTP version number to use for requests" }, { "keytype", keytype, { OPT|NTP_STR, NO, NO, NO }, - { "key type (md5|des)", "", "", "" }, - "set key type to use for authenticated requests (des|md5)" }, + { "key type %s", "", "", "" }, + NULL }, { 0, 0, { NO, NO, NO, NO }, { "", "", "", "" }, "" } }; @@ -361,29 +293,41 @@ struct xcmd builtins[] = { /* * Default values we use. */ -#define DEFTIMEOUT (5) /* 5 second time out */ -#define DEFSTIMEOUT (2) /* 2 second time out after first */ -#define DEFDELAY 0x51EB852 /* 20 milliseconds, l_fp fraction */ #define DEFHOST "localhost" /* default host name */ +#define DEFTIMEOUT 5 /* wait 5 seconds for 1st pkt */ +#define DEFSTIMEOUT 3 /* and 3 more for each additional */ +/* + * Requests are automatically retried once, so total timeout with no + * response is a bit over 2 * DEFTIMEOUT, or 10 seconds. At the other + * extreme, a request eliciting 32 packets of responses each for some + * reason nearly DEFSTIMEOUT seconds after the prior in that series, + * with a single packet dropped, would take around 32 * DEFSTIMEOUT, or + * 93 seconds to fail each of two times, or 186 seconds. + * Some commands involve a series of requests, such as "peers" and + * "mrulist", so the cumulative timeouts are even longer for those. + */ +#define DEFDELAY 0x51EB852 /* 20 milliseconds, l_fp fraction */ #define LENHOSTNAME 256 /* host name is 256 characters long */ #define MAXCMDS 100 /* maximum commands on cmd line */ #define MAXHOSTS 200 /* maximum hosts on cmd line */ #define MAXLINE 512 /* maximum line length */ #define MAXTOKENS (1+MAXARGS+2) /* maximum number of usable tokens */ #define MAXVARLEN 256 /* maximum length of a variable name */ -#define MAXVALLEN 400 /* maximum length of a variable value */ +#define MAXVALLEN 2048 /* maximum length of a variable value */ #define MAXOUTLINE 72 /* maximum length of an output line */ -#define SCREENWIDTH 76 /* nominal screen width in columns */ +#define SCREENWIDTH 76 /* nominal screen width in columns */ /* * Some variables used and manipulated locally */ -struct timeval tvout = { DEFTIMEOUT, 0 }; /* time out for reads */ -struct timeval tvsout = { DEFSTIMEOUT, 0 }; /* secondary time out */ +struct sock_timeval tvout = { DEFTIMEOUT, 0 }; /* time out for reads */ +struct sock_timeval tvsout = { DEFSTIMEOUT, 0 };/* secondary time out */ l_fp delay_time; /* delay time */ char currenthost[LENHOSTNAME]; /* current host name */ -struct sockaddr_in hostaddr = { 0 }; /* host address */ +int currenthostisnum; /* is prior text from IP? */ +struct sockaddr_in hostaddr; /* host address */ int showhostnames = 1; /* show host names by default */ +int wideremote = 0; /* show wide remote names? */ int ai_fam_templ; /* address family */ int ai_fam_default; /* default address family */ @@ -392,13 +336,6 @@ int havehost = 0; /* set to 1 when host open */ int s_port = 0; struct servent *server_entry = NULL; /* server entry for ntp */ -#ifdef SYS_WINNT -DWORD NumberOfBytesWritten; - -HANDLE TimerThreadHandle = NULL; /* 1998/06/03 - Used in ntplib/machines.c */ -void timer(void) { ; }; /* 1998/06/03 - Used in ntplib/machines.c */ - -#endif /* SYS_WINNT */ /* * Sequence number used for requests. It is incremented before @@ -410,15 +347,18 @@ u_short sequence; * Holds data returned from queries. Declare buffer long to be sure of * alignment. */ -#define MAXFRAGS 24 /* maximum number of fragments */ #define DATASIZE (MAXFRAGS*480) /* maximum amount of data */ long pktdata[DATASIZE/sizeof(long)]; /* - * Holds association data for use with the &n operator. + * assoc_cache[] is a dynamic array which allows references to + * associations using &1 ... &N for n associations, avoiding manual + * lookup of the current association IDs for a given ntpd. It also + * caches the status word for each association, retrieved incidentally. */ -struct association assoc_cache[MAXASSOC]; -int numassoc = 0; /* number of cached associations */ +struct association * assoc_cache; +u_int assoc_cache_slots;/* count of allocated array entries */ +u_int numassoc; /* number of cached associations */ /* * For commands typed on the command line (with the -c option) @@ -430,17 +370,18 @@ const char *ccmds[MAXCMDS]; /* * When multiple hosts are specified. */ -int numhosts = 0; -const char *chosts[MAXHOSTS]; -#define ADDHOST(cp) if (numhosts < MAXHOSTS) chosts[numhosts++] = (cp) -/* - * Error codes for internal use - */ -#define ERR_UNSPEC 256 -#define ERR_INCOMPLETE 257 -#define ERR_TIMEOUT 258 -#define ERR_TOOMUCH 259 +u_int numhosts; + +chost chosts[MAXHOSTS]; +#define ADDHOST(cp) \ + do { \ + if (numhosts < MAXHOSTS) { \ + chosts[numhosts].name = (cp); \ + chosts[numhosts].fam = ai_fam_templ; \ + numhosts++; \ + } \ + } while (0) /* * Macro definitions we use @@ -465,23 +406,24 @@ FILE *current_output; extern struct xcmd opcmds[]; char *progname; -volatile int debug; #ifdef NO_MAIN_ALLOWED +#ifndef BUILD_AS_LIB CALL(ntpq,"ntpq",ntpqmain); void clear_globals(void) { - extern int ntp_optind; - showhostnames = 0; /* don'tshow host names by default */ - ntp_optind = 0; - server_entry = NULL; /* server entry for ntp */ - havehost = 0; /* set to 1 when host open */ - numassoc = 0; /* number of cached associations */ - numcmds = 0; - numhosts = 0; + extern int ntp_optind; + showhostnames = 0; /* don'tshow host names by default */ + ntp_optind = 0; + server_entry = NULL; /* server entry for ntp */ + havehost = 0; /* set to 1 when host open */ + numassoc = 0; /* number of cached associations */ + numcmds = 0; + numhosts = 0; } -#endif +#endif /* !BUILD_AS_LIB */ +#endif /* NO_MAIN_ALLOWED */ /* * main - parse arguments and handle options @@ -497,13 +439,16 @@ main( } #endif +#ifndef BUILD_AS_LIB int ntpqmain( int argc, char *argv[] ) { - extern int ntp_optind; + u_int ihost; + int icmd; + #ifdef SYS_VXWORKS clear_globals(); @@ -513,102 +458,99 @@ ntpqmain( delay_time.l_ui = 0; delay_time.l_uf = DEFDELAY; -#ifdef SYS_WINNT - if (!Win32InitSockets()) - { - fprintf(stderr, "No useable winsock.dll:"); - exit(1); - } -#endif /* SYS_WINNT */ + init_lib(); /* sets up ipv4_works, ipv6_works */ + ssl_applink(); + init_auth(); - /* Check to see if we have IPv6. Otherwise force the -4 flag */ - if (isc_net_probeipv6() != ISC_R_SUCCESS) { + /* Check to see if we have IPv6. Otherwise default to IPv4 */ + if (!ipv6_works) ai_fam_default = AF_INET; + + /* Fixup keytype's help based on available digest names */ + + { + char *list; + char *msg, *fmt; + + list = list_digest_names(); + for (icmd = 0; icmd < sizeof(builtins)/sizeof(builtins[0]); icmd++) { + if (strcmp("keytype", builtins[icmd].keyword) == 0) + break; + } + + /* CID: 1295478 */ + /* This should only "trip" if "keytype" is removed from builtins */ + INSIST(icmd < sizeof(builtins)/sizeof(builtins[0])); + +#ifdef OPENSSL + builtins[icmd].desc[0] = "digest-name"; + fmt = "set key type to use for authenticated requests, one of:%s"; +#else + builtins[icmd].desc[0] = "md5"; + fmt = "set key type to use for authenticated requests (%s)"; +#endif + msg = malloc(strlen(fmt) + strlen(list) - strlen("%s") +1); + sprintf(msg, fmt, list); + builtins[icmd].comment = msg; + free(list); } progname = argv[0]; { - int optct = optionProcess(&ntpqOptions, argc, argv); + int optct = ntpOptionProcess(&ntpqOptions, argc, argv); argc -= optct; argv += optct; } - switch (WHICH_IDX_IPV4) { - case INDEX_OPT_IPV4: + /* + * Process options other than -c and -p, which are specially + * handled by ntpq_custom_opt_handler(). + */ + + debug = OPT_VALUE_SET_DEBUG_LEVEL; + + if (HAVE_OPT(IPV4)) ai_fam_templ = AF_INET; - break; - case INDEX_OPT_IPV6: + else if (HAVE_OPT(IPV6)) ai_fam_templ = AF_INET6; - break; - default: + else ai_fam_templ = ai_fam_default; - break; - } - - if (HAVE_OPT(COMMAND)) { - int cmdct = STACKCT_OPT( COMMAND ); - const char** cmds = STACKLST_OPT( COMMAND ); - - while (cmdct-- > 0) { - ADDCMD(*cmds++); - } - } - - debug = DESC(DEBUG_LEVEL).optOccCt; - if (HAVE_OPT(INTERACTIVE)) { + if (HAVE_OPT(INTERACTIVE)) interactive = 1; - } - if (HAVE_OPT(NUMERIC)) { + if (HAVE_OPT(NUMERIC)) showhostnames = 0; - } - if (HAVE_OPT(PEERS)) { - ADDCMD("peers"); - } + if (HAVE_OPT(WIDE)) + wideremote = 1; -#if 0 - while ((c = ntp_getopt(argc, argv, "46c:dinp")) != EOF) - switch (c) { - case '4': - ai_fam_templ = AF_INET; - break; - case '6': - ai_fam_templ = AF_INET6; - break; - case 'c': - ADDCMD(ntp_optarg); - break; - case 'd': - ++debug; - break; - case 'i': - interactive = 1; - break; - case 'n': - showhostnames = 0; - break; - case 'p': - ADDCMD("peers"); - break; - default: - errflg++; - break; - } - if (errflg) { - (void) fprintf(stderr, - "usage: %s [-46dinp] [-c cmd] host ...\n", - progname); - exit(2); - } -#endif - if (ntp_optind == argc) { + old_rv = HAVE_OPT(OLD_RV); + + if (0 == argc) { ADDHOST(DEFHOST); } else { - for (; ntp_optind < argc; ntp_optind++) - ADDHOST(argv[ntp_optind]); + for (ihost = 0; ihost < (u_int)argc; ihost++) { + if ('-' == *argv[ihost]) { + // + // If I really cared I'd also check: + // 0 == argv[ihost][2] + // + // and there are other cases as well... + // + if ('4' == argv[ihost][1]) { + ai_fam_templ = AF_INET; + continue; + } else if ('6' == argv[ihost][1]) { + ai_fam_templ = AF_INET6; + continue; + } else { + // XXX Throw a usage error + } + } + ADDHOST(argv[ihost]); + } } if (numcmds == 0 && interactive == 0 @@ -622,16 +564,13 @@ ntpqmain( #endif /* SYS_WINNT */ if (numcmds == 0) { - (void) openhost(chosts[0]); + (void) openhost(chosts[0].name, chosts[0].fam); getcmds(); } else { - int ihost; - int icmd; - for (ihost = 0; ihost < numhosts; ihost++) { - if (openhost(chosts[ihost])) - for (icmd = 0; icmd < numcmds; icmd++) - docmd(ccmds[icmd]); + if (openhost(chosts[ihost].name, chosts[ihost].fam)) + for (icmd = 0; icmd < numcmds; icmd++) + docmd(ccmds[icmd]); } } #ifdef SYS_WINNT @@ -639,35 +578,42 @@ ntpqmain( #endif /* SYS_WINNT */ return 0; } - +#endif /* !BUILD_AS_LIB */ /* * openhost - open a socket to a host */ -static int +static int openhost( - const char *hname + const char *hname, + int fam ) { + const char svc[] = "ntp"; char temphost[LENHOSTNAME]; int a_info, i; - struct addrinfo hints, *ai = NULL; + struct addrinfo hints, *ai; + sockaddr_u addr; + size_t octets; register const char *cp; char name[LENHOSTNAME]; - char service[5]; /* * We need to get by the [] if they were entered */ - + cp = hname; - - if(*cp == '[') { + + if (*cp == '[') { cp++; - for(i = 0; *cp != ']'; cp++, i++) + for (i = 0; *cp && *cp != ']'; cp++, i++) name[i] = *cp; - name[i] = '\0'; - hname = name; + if (*cp == ']') { + name[i] = '\0'; + hname = name; + } else { + return 0; + } } /* @@ -677,14 +623,14 @@ openhost( * will return an "IPv4-mapped IPv6 address" address if you * give it an IPv4 address to lookup. */ - strcpy(service, "ntp"); - memset((char *)&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = ai_fam_templ; + ZERO(hints); + hints.ai_family = fam; hints.ai_protocol = IPPROTO_UDP; hints.ai_socktype = SOCK_DGRAM; - hints.ai_flags = AI_NUMERICHOST; + hints.ai_flags = Z_AI_NUMERICHOST; + ai = NULL; - a_info = getaddrinfo(hname, service, &hints, &ai); + a_info = getaddrinfo(hname, svc, &hints, &ai); if (a_info == EAI_NONAME #ifdef EAI_NODATA || a_info == EAI_NODATA @@ -694,41 +640,53 @@ openhost( #ifdef AI_ADDRCONFIG hints.ai_flags |= AI_ADDRCONFIG; #endif - a_info = getaddrinfo(hname, service, &hints, &ai); + a_info = getaddrinfo(hname, svc, &hints, &ai); } +#ifdef AI_ADDRCONFIG /* Some older implementations don't like AI_ADDRCONFIG. */ if (a_info == EAI_BADFLAGS) { - hints.ai_flags = AI_CANONNAME; - a_info = getaddrinfo(hname, service, &hints, &ai); + hints.ai_flags &= ~AI_ADDRCONFIG; + a_info = getaddrinfo(hname, svc, &hints, &ai); } +#endif if (a_info != 0) { - (void) fprintf(stderr, "%s\n", gai_strerror(a_info)); + fprintf(stderr, "%s\n", gai_strerror(a_info)); return 0; } - if (ai->ai_canonname == NULL) { - strncpy(temphost, stoa((struct sockaddr_storage *)ai->ai_addr), - LENHOSTNAME); - temphost[LENHOSTNAME-1] = '\0'; + INSIST(ai != NULL); + ZERO(addr); + octets = min(sizeof(addr), ai->ai_addrlen); + memcpy(&addr, ai->ai_addr, octets); + if (ai->ai_canonname == NULL) { + strlcpy(temphost, stoa(&addr), sizeof(temphost)); + currenthostisnum = TRUE; } else { - strncpy(temphost, ai->ai_canonname, LENHOSTNAME); - temphost[LENHOSTNAME-1] = '\0'; + strlcpy(temphost, ai->ai_canonname, sizeof(temphost)); + currenthostisnum = FALSE; } if (debug > 2) - printf("Opening host %s\n", temphost); + printf("Opening host %s (%s)\n", + temphost, + (ai->ai_family == AF_INET) + ? "AF_INET" + : (ai->ai_family == AF_INET6) + ? "AF_INET6" + : "AF-???" + ); if (havehost == 1) { if (debug > 2) - printf("Closing old host %s\n", currenthost); - (void) closesocket(sockfd); + printf("Closing old host %s\n", currenthost); + closesocket(sockfd); havehost = 0; } - (void) strcpy(currenthost, temphost); + strlcpy(currenthost, temphost, sizeof(currenthost)); /* port maps to the same location in both families */ - s_port = ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port; + s_port = NSRCPORT(&addr); #ifdef SYS_VXWORKS ((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM); if (ai->ai_family == AF_INET) @@ -744,81 +702,118 @@ openhost( int optionValue = SO_SYNCHRONOUS_NONALERT; int err; - err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&optionValue, sizeof(optionValue)); - if (err != NO_ERROR) { - (void) fprintf(stderr, "cannot open nonoverlapped sockets\n"); + err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, + (char *)&optionValue, sizeof(optionValue)); + if (err) { + mfprintf(stderr, + "setsockopt(SO_SYNCHRONOUS_NONALERT)" + " error: %m\n"); + freeaddrinfo(ai); exit(1); } } #endif /* SYS_WINNT */ - sockfd = socket(ai->ai_family, SOCK_DGRAM, 0); + sockfd = socket(ai->ai_family, ai->ai_socktype, + ai->ai_protocol); if (sockfd == INVALID_SOCKET) { - error("socket", "", ""); + error("socket"); + freeaddrinfo(ai); + return 0; } - + #ifdef NEED_RCVBUF_SLOP # ifdef SO_RCVBUF { int rbufsize = DATASIZE + 2048; /* 2K for slop */ if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rbufsize, sizeof(int)) == -1) - error("setsockopt", "", ""); + error("setsockopt"); } # endif #endif + if #ifdef SYS_VXWORKS - if (connect(sockfd, (struct sockaddr *)&hostaddr, + (connect(sockfd, (struct sockaddr *)&hostaddr, sizeof(hostaddr)) == -1) #else - if (connect(sockfd, (struct sockaddr *)ai->ai_addr, + (connect(sockfd, (struct sockaddr *)ai->ai_addr, ai->ai_addrlen) == -1) #endif /* SYS_VXWORKS */ - error("connect", "", ""); - if (a_info == 0) + { + error("connect"); freeaddrinfo(ai); + return 0; + } + freeaddrinfo(ai); havehost = 1; + numassoc = 0; + return 1; } +static void +dump_hex_printable( + const void * data, + size_t len + ) +{ + const char * cdata; + const char * rowstart; + size_t idx; + size_t rowlen; + u_char uch; + + cdata = data; + while (len > 0) { + rowstart = cdata; + rowlen = min(16, len); + for (idx = 0; idx < rowlen; idx++) { + uch = *(cdata++); + printf("%02x ", uch); + } + for ( ; idx < 16 ; idx++) + printf(" "); + cdata = rowstart; + for (idx = 0; idx < rowlen; idx++) { + uch = *(cdata++); + printf("%c", (isprint(uch)) + ? uch + : '.'); + } + printf("\n"); + len -= rowlen; + } +} + + /* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */ /* * sendpkt - send a packet to the remote host */ static int sendpkt( - char *xdata, - int xdatalen + void * xdata, + size_t xdatalen ) { if (debug >= 3) - printf("Sending %d octets\n", xdatalen); - + printf("Sending %zu octets\n", xdatalen); if (send(sockfd, xdata, (size_t)xdatalen, 0) == -1) { - warning("write to %s failed", currenthost, ""); + warning("write to %s failed", currenthost); return -1; } if (debug >= 4) { - int first = 8; - printf("Packet data:\n"); - while (xdatalen-- > 0) { - if (first-- == 0) { - printf("\n"); - first = 7; - } - printf(" %02x", *xdata++ & 0xff); - } - printf("\n"); + printf("Request packet:\n"); + dump_hex_printable(xdata, xdatalen); } return 0; } - - /* * getresponse - get a (series of) response packet(s) and return the data */ @@ -828,20 +823,24 @@ getresponse( int associd, u_short *rstatus, int *rsize, - char **rdata, + const char **rdata, int timeo ) { struct ntp_control rpkt; - struct timeval tvo; + struct sock_timeval tvo; u_short offsets[MAXFRAGS+1]; u_short counts[MAXFRAGS+1]; u_short offset; u_short count; - int numfrags; + size_t numfrags; + size_t f; + size_t ff; int seenlastfrag; + int shouldbesize; fd_set fds; int n; + int errcode; /* * This is pretty tricky. We may get between 1 and MAXFRAG packets @@ -852,7 +851,7 @@ getresponse( */ *rsize = 0; if (rstatus) - *rstatus = 0; + *rstatus = 0; *rdata = (char *)pktdata; numfrags = 0; @@ -860,305 +859,308 @@ getresponse( FD_ZERO(&fds); - again: - if (numfrags == 0) - tvo = tvout; - else - tvo = tvsout; - - FD_SET(sockfd, &fds); - n = select(sockfd+1, &fds, (fd_set *)0, (fd_set *)0, &tvo); - -#if 0 - if (debug >= 1) - printf("select() returns %d\n", n); -#endif + /* + * Loop until we have an error or a complete response. Nearly all + * code paths to loop again use continue. + */ + for (;;) { - if (n == -1) { - warning("select fails", "", ""); - return -1; - } - if (n == 0) { - /* - * Timed out. Return what we have - */ - if (numfrags == 0) { - if (timeo) - (void) fprintf(stderr, - "%s: timed out, nothing received\n", - currenthost); - return ERR_TIMEOUT; - } else { + if (numfrags == 0) + tvo = tvout; + else + tvo = tvsout; + + FD_SET(sockfd, &fds); + n = select(sockfd + 1, &fds, NULL, NULL, &tvo); + + if (n == -1) { + warning("select fails"); + return -1; + } + if (n == 0) { + /* + * Timed out. Return what we have + */ + if (numfrags == 0) { + if (timeo) + fprintf(stderr, + "%s: timed out, nothing received\n", + currenthost); + return ERR_TIMEOUT; + } if (timeo) - (void) fprintf(stderr, - "%s: timed out with incomplete data\n", - currenthost); + fprintf(stderr, + "%s: timed out with incomplete data\n", + currenthost); if (debug) { - printf("Received fragments:\n"); - for (n = 0; n < numfrags; n++) - printf("%4d %d\n", offsets[n], - counts[n]); - if (seenlastfrag) - printf("last fragment received\n"); - else - printf("last fragment not received\n"); + fprintf(stderr, + "ERR_INCOMPLETE: Received fragments:\n"); + for (f = 0; f < numfrags; f++) + fprintf(stderr, + "%2u: %5d %5d\t%3d octets\n", + (u_int)f, offsets[f], + offsets[f] + + counts[f], + counts[f]); + fprintf(stderr, + "last fragment %sreceived\n", + (seenlastfrag) + ? "" + : "not "); } return ERR_INCOMPLETE; } - } - - n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0); - if (n == -1) { - warning("read", "", ""); - return -1; - } - if (debug >= 4) { - int len = n, first = 8; - char *data = (char *)&rpkt; - - printf("Packet data:\n"); - while (len-- > 0) { - if (first-- == 0) { - printf("\n"); - first = 7; - } - printf(" %02x", *data++ & 0xff); + n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0); + if (n == -1) { + warning("read"); + return -1; } - printf("\n"); - } - /* - * Check for format errors. Bug proofing. - */ - if (n < CTL_HEADER_LEN) { - if (debug) - printf("Short (%d byte) packet received\n", n); - goto again; - } - if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION - || PKT_VERSION(rpkt.li_vn_mode) < NTP_OLDVERSION) { - if (debug) - printf("Packet received with version %d\n", - PKT_VERSION(rpkt.li_vn_mode)); - goto again; - } - if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) { - if (debug) - printf("Packet received with mode %d\n", - PKT_MODE(rpkt.li_vn_mode)); - goto again; - } - if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) { - if (debug) - printf("Received request packet, wanted response\n"); - goto again; - } + if (debug >= 4) { + printf("Response packet:\n"); + dump_hex_printable(&rpkt, n); + } - /* - * Check opcode and sequence number for a match. - * Could be old data getting to us. - */ - if (ntohs(rpkt.sequence) != sequence) { - if (debug) - printf( - "Received sequnce number %d, wanted %d\n", - ntohs(rpkt.sequence), sequence); - goto again; - } - if (CTL_OP(rpkt.r_m_e_op) != opcode) { - if (debug) - printf( - "Received opcode %d, wanted %d (sequence number okay)\n", - CTL_OP(rpkt.r_m_e_op), opcode); - goto again; - } + /* + * Check for format errors. Bug proofing. + */ + if (n < (int)CTL_HEADER_LEN) { + if (debug) + printf("Short (%d byte) packet received\n", n); + continue; + } + if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION + || PKT_VERSION(rpkt.li_vn_mode) < NTP_OLDVERSION) { + if (debug) + printf("Packet received with version %d\n", + PKT_VERSION(rpkt.li_vn_mode)); + continue; + } + if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) { + if (debug) + printf("Packet received with mode %d\n", + PKT_MODE(rpkt.li_vn_mode)); + continue; + } + if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) { + if (debug) + printf("Received request packet, wanted response\n"); + continue; + } - /* - * Check the error code. If non-zero, return it. - */ - if (CTL_ISERROR(rpkt.r_m_e_op)) { - int errcode; + /* + * Check opcode and sequence number for a match. + * Could be old data getting to us. + */ + if (ntohs(rpkt.sequence) != sequence) { + if (debug) + printf("Received sequnce number %d, wanted %d\n", + ntohs(rpkt.sequence), sequence); + continue; + } + if (CTL_OP(rpkt.r_m_e_op) != opcode) { + if (debug) + printf( + "Received opcode %d, wanted %d (sequence number okay)\n", + CTL_OP(rpkt.r_m_e_op), opcode); + continue; + } - errcode = (ntohs(rpkt.status) >> 8) & 0xff; - if (debug && CTL_ISMORE(rpkt.r_m_e_op)) { - printf("Error code %d received on not-final packet\n", - errcode); + /* + * Check the error code. If non-zero, return it. + */ + if (CTL_ISERROR(rpkt.r_m_e_op)) { + errcode = (ntohs(rpkt.status) >> 8) & 0xff; + if (CTL_ISMORE(rpkt.r_m_e_op)) + TRACE(1, ("Error code %d received on not-final packet\n", + errcode)); + if (errcode == CERR_UNSPEC) + return ERR_UNSPEC; + return errcode; } - if (errcode == CERR_UNSPEC) - return ERR_UNSPEC; - return errcode; - } - /* - * Check the association ID to make sure it matches what - * we sent. - */ - if (ntohs(rpkt.associd) != associd) { - if (debug) - printf("Association ID %d doesn't match expected %d\n", - ntohs(rpkt.associd), associd); /* - * Hack for silly fuzzballs which, at the time of writing, - * return an assID of sys.peer when queried for system variables. + * Check the association ID to make sure it matches what + * we sent. */ + if (ntohs(rpkt.associd) != associd) { + TRACE(1, ("Association ID %d doesn't match expected %d\n", + ntohs(rpkt.associd), associd)); + /* + * Hack for silly fuzzballs which, at the time of writing, + * return an assID of sys.peer when queried for system variables. + */ #ifdef notdef - goto again; + continue; #endif - } - - /* - * Collect offset and count. Make sure they make sense. - */ - offset = ntohs(rpkt.offset); - count = ntohs(rpkt.count); + } - if (debug >= 3) { - int shouldbesize; - u_long key; - u_long *lpkt; - int maclen; + /* + * Collect offset and count. Make sure they make sense. + */ + offset = ntohs(rpkt.offset); + count = ntohs(rpkt.count); /* - * Usually we ignore authentication, but for debugging purposes - * we watch it here. + * validate received payload size is padded to next 32-bit + * boundary and no smaller than claimed by rpkt.count */ - shouldbesize = CTL_HEADER_LEN + count; + if (n & 0x3) { + TRACE(1, ("Response packet not padded, size = %d\n", + n)); + continue; + } - /* round to 8 octet boundary */ - shouldbesize = (shouldbesize + 7) & ~7; + shouldbesize = (CTL_HEADER_LEN + count + 3) & ~3; - if (n & 0x3) { - printf("Packet not padded, size = %d\n", n); - } if ((maclen = n - shouldbesize) >= MIN_MAC_LEN) { - printf( - "Packet shows signs of authentication (total %d, data %d, mac %d)\n", - n, shouldbesize, maclen); - lpkt = (u_long *)&rpkt; - printf("%08lx %08lx %08lx %08lx %08lx %08lx\n", - (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 3]), - (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 2]), - (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 1]), - (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long)]), - (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) + 1]), - (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) + 2])); - key = ntohl(lpkt[(n - maclen) / sizeof(u_long)]); - printf("Authenticated with keyid %lu\n", (u_long)key); - if (key != 0 && key != info_auth_keyid) { - printf("We don't know that key\n"); - } else { - if (authdecrypt(key, (u_int32 *)&rpkt, - n - maclen, maclen)) { - printf("Auth okay!\n"); + if (n < shouldbesize) { + printf("Response packet claims %u octets payload, above %ld received\n", + count, (long)n - CTL_HEADER_LEN); + return ERR_INCOMPLETE; + } + + if (debug >= 3 && shouldbesize > n) { + u_int32 key; + u_int32 *lpkt; + int maclen; + + /* + * Usually we ignore authentication, but for debugging purposes + * we watch it here. + */ + /* round to 8 octet boundary */ + shouldbesize = (shouldbesize + 7) & ~7; + + maclen = n - shouldbesize; + if (maclen >= (int)MIN_MAC_LEN) { + printf( + "Packet shows signs of authentication (total %d, data %d, mac %d)\n", + n, shouldbesize, maclen); + lpkt = (u_int32 *)&rpkt; + printf("%08lx %08lx %08lx %08lx %08lx %08lx\n", + (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 3]), + (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 2]), + (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 1]), + (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32)]), + (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 1]), + (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 2])); + key = ntohl(lpkt[(n - maclen) / sizeof(u_int32)]); + printf("Authenticated with keyid %lu\n", (u_long)key); + if (key != 0 && key != info_auth_keyid) { + printf("We don't know that key\n"); } else { - printf("Auth failed!\n"); + if (authdecrypt(key, (u_int32 *)&rpkt, + n - maclen, maclen)) { + printf("Auth okay!\n"); + } else { + printf("Auth failed!\n"); + } } } } - } - if (debug >= 2) - printf("Got packet, size = %d\n", n); - if (count > (u_short)(n-CTL_HEADER_LEN)) { - if (debug) - printf( - "Received count of %d octets, data in packet is %d\n", - count, n-CTL_HEADER_LEN); - goto again; - } - if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) { - if (debug) - printf("Received count of 0 in non-final fragment\n"); - goto again; - } - if (offset + count > sizeof(pktdata)) { - if (debug) - printf("Offset %d, count %d, too big for buffer\n", - offset, count); - return ERR_TOOMUCH; - } - if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) { - if (debug) - printf("Received second last fragment packet\n"); - goto again; - } + TRACE(2, ("Got packet, size = %d\n", n)); + if (count > (n - CTL_HEADER_LEN)) { + TRACE(1, ("Received count of %u octets, data in packet is %ld\n", + count, (long)n - CTL_HEADER_LEN)); + continue; + } + if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) { + TRACE(1, ("Received count of 0 in non-final fragment\n")); + continue; + } + if (offset + count > sizeof(pktdata)) { + TRACE(1, ("Offset %u, count %u, too big for buffer\n", + offset, count)); + return ERR_TOOMUCH; + } + if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) { + TRACE(1, ("Received second last fragment packet\n")); + continue; + } - /* - * So far, so good. Record this fragment, making sure it doesn't - * overlap anything. - */ - if (debug >= 2) - printf("Packet okay\n");; - - if (numfrags == MAXFRAGS) { - if (debug) - printf("Number of fragments exceeds maximum\n"); - return ERR_TOOMUCH; - } - - for (n = 0; n < numfrags; n++) { - if (offset == offsets[n]) - goto again; /* duplicate */ - if (offset < offsets[n]) - break; - } - - if ((u_short)(n > 0 && offsets[n-1] + counts[n-1]) > offset) - goto overlap; - if (n < numfrags && (u_short)(offset + count) > offsets[n]) - goto overlap; - - { - register int i; - - for (i = numfrags; i > n; i--) { - offsets[i] = offsets[i-1]; - counts[i] = counts[i-1]; + /* + * So far, so good. Record this fragment, making sure it doesn't + * overlap anything. + */ + TRACE(2, ("Packet okay\n")); + + if (numfrags > (MAXFRAGS - 1)) { + TRACE(2, ("Number of fragments exceeds maximum %d\n", + MAXFRAGS - 1)); + return ERR_TOOMUCH; } - } - offsets[n] = offset; - counts[n] = count; - numfrags++; - /* - * Got that stuffed in right. Figure out if this was the last. - * Record status info out of the last packet. - */ - if (!CTL_ISMORE(rpkt.r_m_e_op)) { - seenlastfrag = 1; - if (rstatus != 0) - *rstatus = ntohs(rpkt.status); - } + /* + * Find the position for the fragment relative to any + * previously received. + */ + for (f = 0; + f < numfrags && offsets[f] < offset; + f++) { + /* empty body */ ; + } - /* - * Copy the data into the data buffer. - */ - memmove((char *)pktdata + offset, (char *)rpkt.data, count); + if (f < numfrags && offset == offsets[f]) { + TRACE(1, ("duplicate %u octets at %u ignored, prior %u at %u\n", + count, offset, counts[f], offsets[f])); + continue; + } - /* - * If we've seen the last fragment, look for holes in the sequence. - * If there aren't any, we're done. - */ - if (seenlastfrag && offsets[0] == 0) { - for (n = 1; n < numfrags; n++) { - if (offsets[n-1] + counts[n-1] != offsets[n]) - break; + if (f > 0 && (offsets[f-1] + counts[f-1]) > offset) { + TRACE(1, ("received frag at %u overlaps with %u octet frag at %u\n", + offset, counts[f-1], offsets[f-1])); + continue; } - if (n == numfrags) { - *rsize = offsets[numfrags-1] + counts[numfrags-1]; - return 0; + + if (f < numfrags && (offset + count) > offsets[f]) { + TRACE(1, ("received %u octet frag at %u overlaps with frag at %u\n", + count, offset, offsets[f])); + continue; } - } - goto again; - overlap: - /* - * Print debugging message about overlapping fragments - */ - if (debug) - printf("Overlapping fragments returned in response\n"); - goto again; -} + for (ff = numfrags; ff > f; ff--) { + offsets[ff] = offsets[ff-1]; + counts[ff] = counts[ff-1]; + } + offsets[f] = offset; + counts[f] = count; + numfrags++; + + /* + * Got that stuffed in right. Figure out if this was the last. + * Record status info out of the last packet. + */ + if (!CTL_ISMORE(rpkt.r_m_e_op)) { + seenlastfrag = 1; + if (rstatus != 0) + *rstatus = ntohs(rpkt.status); + } + + /* + * Copy the data into the data buffer. + */ + memcpy((char *)pktdata + offset, &rpkt.u, count); + + /* + * If we've seen the last fragment, look for holes in the sequence. + * If there aren't any, we're done. + */ + if (seenlastfrag && offsets[0] == 0) { + for (f = 1; f < numfrags; f++) + if (offsets[f-1] + counts[f-1] != + offsets[f]) + break; + if (f == numfrags) { + *rsize = offsets[f-1] + counts[f-1]; + TRACE(1, ("%lu packets reassembled into response\n", + (u_long)numfrags)); + return 0; + } + } + } /* giant for (;;) collecting response packets */ +} /* getresponse() */ /* @@ -1167,22 +1169,25 @@ getresponse( static int sendrequest( int opcode, - int associd, + associd_t associd, int auth, int qsize, - char *qdata + const char *qdata ) { struct ntp_control qpkt; - int pktsize; + int pktsize; + u_long key_id; + char * pass; + int maclen; /* * Check to make sure the data will fit in one packet */ if (qsize > CTL_MAX_DATA_LEN) { - (void) fprintf(stderr, - "***Internal error! qsize (%d) too large\n", - qsize); + fprintf(stderr, + "***Internal error! qsize (%d) too large\n", + qsize); return 1; } @@ -1197,19 +1202,18 @@ sendrequest( qpkt.offset = 0; qpkt.count = htons((u_short)qsize); + pktsize = CTL_HEADER_LEN; + /* - * If we have data, copy it in and pad it out to a 64 - * bit boundary. + * If we have data, copy and pad it out to a 32-bit boundary. */ if (qsize > 0) { - memmove((char *)qpkt.data, qdata, (unsigned)qsize); - pktsize = qsize + CTL_HEADER_LEN; - while (pktsize & (sizeof(u_long) - 1)) { - qpkt.data[qsize++] = 0; + memcpy(&qpkt.u, qdata, (size_t)qsize); + pktsize += qsize; + while (pktsize & (sizeof(u_int32) - 1)) { + qpkt.u.data[qsize++] = 0; pktsize++; } - } else { - pktsize = CTL_HEADER_LEN; } /* @@ -1217,76 +1221,167 @@ sendrequest( * we're going to have to think about it a little. */ if (!auth && !always_auth) { - return sendpkt((char *)&qpkt, pktsize); - } else { - const char *pass = "\0"; - int maclen = 0; - u_long my_keyid; + return sendpkt(&qpkt, pktsize); + } - /* - * Pad out packet to a multiple of 8 octets to be sure - * receiver can handle it. - */ - while (pktsize & 7) { - qpkt.data[qsize++] = 0; - pktsize++; - } + /* + * Pad out packet to a multiple of 8 octets to be sure + * receiver can handle it. + */ + while (pktsize & 7) { + qpkt.u.data[qsize++] = 0; + pktsize++; + } - /* - * Get the keyid and the password if we don't have one. - */ - if (info_auth_keyid == 0) { - int u_keyid = getkeyid("Keyid: "); - if (u_keyid == 0 || u_keyid > NTP_MAXKEY) { - (void) fprintf(stderr, - "Invalid key identifier\n"); - return 1; - } - info_auth_keyid = u_keyid; - } - if (!authistrusted(info_auth_keyid)) { - pass = getpass("MD5 Password: "); - if (*pass == '\0') { - (void) fprintf(stderr, - "Invalid password\n"); - return (1); - } + /* + * Get the keyid and the password if we don't have one. + */ + if (info_auth_keyid == 0) { + key_id = getkeyid("Keyid: "); + if (key_id == 0 || key_id > NTP_MAXKEY) { + fprintf(stderr, + "Invalid key identifier\n"); + return 1; + } + info_auth_keyid = key_id; + } + if (!authistrusted(info_auth_keyid)) { + pass = getpass_keytype(info_auth_keytype); + if ('\0' == pass[0]) { + fprintf(stderr, "Invalid password\n"); + return 1; } - authusekey(info_auth_keyid, info_auth_keytype, (const u_char *)pass); + authusekey(info_auth_keyid, info_auth_keytype, + (u_char *)pass); authtrust(info_auth_keyid, 1); + } - /* - * Stick the keyid in the packet where - * cp currently points. Cp should be aligned - * properly. Then do the encryptions. - */ - my_keyid = htonl(info_auth_keyid); - memcpy(&qpkt.data[qsize], &my_keyid, sizeof my_keyid); - maclen = authencrypt(info_auth_keyid, (u_int32 *)&qpkt, - pktsize); - if (maclen == 0) { - (void) fprintf(stderr, "Key not found\n"); - return (1); - } - return sendpkt((char *)&qpkt, pktsize + maclen); + /* + * Do the encryption. + */ + maclen = authencrypt(info_auth_keyid, (void *)&qpkt, pktsize); + if (!maclen) { + fprintf(stderr, "Key not found\n"); + return 1; + } else if ((size_t)maclen != (info_auth_hashlen + sizeof(keyid_t))) { + fprintf(stderr, + "%d octet MAC, %zu expected with %zu octet digest\n", + maclen, (info_auth_hashlen + sizeof(keyid_t)), + info_auth_hashlen); + return 1; } - /*NOTREACHED*/ + + return sendpkt((char *)&qpkt, pktsize + maclen); } /* - * doquery - send a request and process the response + * show_error_msg - display the error text for a mode 6 error response. + */ +void +show_error_msg( + int m6resp, + associd_t associd + ) +{ + if (numhosts > 1) + fprintf(stderr, "server=%s ", currenthost); + + switch(m6resp) { + + case CERR_BADFMT: + fprintf(stderr, + "***Server reports a bad format request packet\n"); + break; + + case CERR_PERMISSION: + fprintf(stderr, + "***Server disallowed request (authentication?)\n"); + break; + + case CERR_BADOP: + fprintf(stderr, + "***Server reports a bad opcode in request\n"); + break; + + case CERR_BADASSOC: + fprintf(stderr, + "***Association ID %d unknown to server\n", + associd); + break; + + case CERR_UNKNOWNVAR: + fprintf(stderr, + "***A request variable unknown to the server\n"); + break; + + case CERR_BADVALUE: + fprintf(stderr, + "***Server indicates a request variable was bad\n"); + break; + + case ERR_UNSPEC: + fprintf(stderr, + "***Server returned an unspecified error\n"); + break; + + case ERR_TIMEOUT: + fprintf(stderr, "***Request timed out\n"); + break; + + case ERR_INCOMPLETE: + fprintf(stderr, + "***Response from server was incomplete\n"); + break; + + case ERR_TOOMUCH: + fprintf(stderr, + "***Buffer size exceeded for returned data\n"); + break; + + default: + fprintf(stderr, + "***Server returns unknown error code %d\n", + m6resp); + } +} + +/* + * doquery - send a request and process the response, displaying + * error messages for any error responses. */ int doquery( int opcode, - int associd, + associd_t associd, + int auth, + int qsize, + const char *qdata, + u_short *rstatus, + int *rsize, + const char **rdata + ) +{ + return doqueryex(opcode, associd, auth, qsize, qdata, rstatus, + rsize, rdata, FALSE); +} + + +/* + * doqueryex - send a request and process the response, optionally + * displaying error messages for any error responses. + */ +int +doqueryex( + int opcode, + associd_t associd, int auth, int qsize, - char *qdata, + const char *qdata, u_short *rstatus, int *rsize, - char **rdata + const char **rdata, + int quiet ) { int res; @@ -1296,7 +1391,7 @@ doquery( * Check to make sure host is open */ if (!havehost) { - (void) fprintf(stderr, "***No host open, use `host' command\n"); + fprintf(stderr, "***No host open, use `host' command\n"); return -1; } @@ -1309,8 +1404,8 @@ doquery( */ res = sendrequest(opcode, associd, auth, qsize, qdata); if (res != 0) - return res; - + return res; + /* * Get the response. If we got a standard error, print a message */ @@ -1328,94 +1423,40 @@ doquery( done = 1; goto again; } - if (numhosts > 1) - (void) fprintf(stderr, "server=%s ", currenthost); - switch(res) { - case CERR_BADFMT: - (void) fprintf(stderr, - "***Server reports a bad format request packet\n"); - break; - case CERR_PERMISSION: - (void) fprintf(stderr, - "***Server disallowed request (authentication?)\n"); - break; - case CERR_BADOP: - (void) fprintf(stderr, - "***Server reports a bad opcode in request\n"); - break; - case CERR_BADASSOC: - (void) fprintf(stderr, - "***Association ID %d unknown to server\n",associd); - break; - case CERR_UNKNOWNVAR: - (void) fprintf(stderr, - "***A request variable unknown to the server\n"); - break; - case CERR_BADVALUE: - (void) fprintf(stderr, - "***Server indicates a request variable was bad\n"); - break; - case ERR_UNSPEC: - (void) fprintf(stderr, - "***Server returned an unspecified error\n"); - break; - case ERR_TIMEOUT: - (void) fprintf(stderr, "***Request timed out\n"); - break; - case ERR_INCOMPLETE: - (void) fprintf(stderr, - "***Response from server was incomplete\n"); - break; - case ERR_TOOMUCH: - (void) fprintf(stderr, - "***Buffer size exceeded for returned data\n"); - break; - default: - (void) fprintf(stderr, - "***Server returns unknown error code %d\n", res); - break; - } + if (!quiet) + show_error_msg(res, associd); + } return res; } +#ifndef BUILD_AS_LIB /* * getcmds - read commands from the standard input and execute them */ static void getcmds(void) { -#if defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDIT) - char *line; - - for (;;) { - if ((line = readline(interactive?prompt:"")) == NULL) return; - if (*line) add_history(line); - docmd(line); - free(line); - } -#else /* not (HAVE_LIBREADLINE || HAVE_LIBEDIT) */ - char line[MAXLINE]; - - for (;;) { - if (interactive) { -#ifdef VMS /* work around a problem with mixing stdout & stderr */ - fputs("",stdout); -#endif - (void) fputs(prompt, stderr); - (void) fflush(stderr); - } + char * line; + int count; + + ntp_readline_init(interactive ? prompt : NULL); - if (fgets(line, sizeof line, stdin) == NULL) - return; + for (;;) { + line = ntp_readline(&count); + if (NULL == line) + break; + docmd(line); + free(line); + } - docmd(line); - } -#endif /* not (HAVE_LIBREADLINE || HAVE_LIBEDIT) */ + ntp_readline_uninit(); } +#endif /* !BUILD_AS_LIB */ -#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */ + +#if !defined(SYS_WINNT) && !defined(BUILD_AS_LIB) /* * abortcmd - catch interrupts and abort the current command */ @@ -1430,8 +1471,10 @@ abortcmd( (void) fflush(stderr); if (jump) longjmp(interrupt_buf, 1); } -#endif /* SYS_WINNT */ +#endif /* !SYS_WINNT && !BUILD_AS_LIB */ + +#ifndef BUILD_AS_LIB /* * docmd - decode the command line and execute a command */ @@ -1452,7 +1495,7 @@ docmd( tokenize(cmdline, tokens, &ntok); if (ntok == 0) return; - + /* * Find the appropriate command description. */ @@ -1466,7 +1509,12 @@ docmd( tokens[0]); return; } - + + /* Warn about ignored extra args */ + for (i = MAXARGS + 1; i < ntok ; ++i) { + fprintf(stderr, "***Extra arg `%s' ignored\n", tokens[i]); + } + /* * Save the keyword, then walk through the arguments, interpreting * as we go. @@ -1482,9 +1530,9 @@ docmd( break; } if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>')) - break; + break; if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i])) - return; + return; pcmd.nargs++; } @@ -1493,9 +1541,9 @@ docmd( char *fname; if (*(tokens[i]+1) != '\0') - fname = tokens[i]+1; + fname = tokens[i]+1; else if ((i+1) < ntok) - fname = tokens[i+1]; + fname = tokens[i+1]; else { (void) fprintf(stderr, "***No file for redirect\n"); return; @@ -1522,12 +1570,20 @@ docmd( jump = 0; /* HMS: 961106: was after fclose() */ if (i) (void) fclose(current_output); } + + return; } /* * tokenize - turn a command line into tokens + * + * SK: Modified to allow a quoted string + * + * HMS: If the first character of the first token is a ':' then (after + * eating inter-token whitespace) the 2nd token is the rest of the line. */ + static void tokenize( const char *line, @@ -1543,33 +1599,151 @@ tokenize( cp = line; for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) { tokens[*ntok] = sp; + + /* Skip inter-token whitespace */ while (ISSPACE(*cp)) cp++; + + /* If we're at EOL we're done */ if (ISEOL(*cp)) break; - do { - *sp++ = *cp++; - } while (!ISSPACE(*cp) && !ISEOL(*cp)); + /* If this is the 2nd token and the first token begins + * with a ':', then just grab to EOL. + */ + + if (*ntok == 1 && tokens[0][0] == ':') { + do { + if (sp - tspace >= MAXLINE) + goto toobig; + *sp++ = *cp++; + } while (!ISEOL(*cp)); + } + + /* Check if this token begins with a double quote. + * If yes, continue reading till the next double quote + */ + else if (*cp == '\"') { + ++cp; + do { + if (sp - tspace >= MAXLINE) + goto toobig; + *sp++ = *cp++; + } while ((*cp != '\"') && !ISEOL(*cp)); + /* HMS: a missing closing " should be an error */ + } + else { + do { + if (sp - tspace >= MAXLINE) + goto toobig; + *sp++ = *cp++; + } while ((*cp != '\"') && !ISSPACE(*cp) && !ISEOL(*cp)); + /* HMS: Why check for a " in the previous line? */ + } + + if (sp - tspace >= MAXLINE) + goto toobig; *sp++ = '\0'; } + return; + + toobig: + *ntok = 0; + fprintf(stderr, + "***Line `%s' is too big\n", + line); + return; } +/* + * getarg - interpret an argument token + */ +static int +getarg( + const char *str, + int code, + arg_v *argp + ) +{ + u_long ul; + + switch (code & ~OPT) { + case NTP_STR: + argp->string = str; + break; + + case NTP_ADD: + if (!getnetnum(str, &argp->netnum, NULL, 0)) + return 0; + break; + + case NTP_UINT: + if ('&' == str[0]) { + if (!atouint(&str[1], &ul)) { + fprintf(stderr, + "***Association index `%s' invalid/undecodable\n", + str); + return 0; + } + if (0 == numassoc) { + dogetassoc(stdout); + if (0 == numassoc) { + fprintf(stderr, + "***No associations found, `%s' unknown\n", + str); + return 0; + } + } + ul = min(ul, numassoc); + argp->uval = assoc_cache[ul - 1].assid; + break; + } + if (!atouint(str, &argp->uval)) { + fprintf(stderr, "***Illegal unsigned value %s\n", + str); + return 0; + } + break; + + case NTP_INT: + if (!atoint(str, &argp->ival)) { + fprintf(stderr, "***Illegal integer value %s\n", + str); + return 0; + } + break; + + case IP_VERSION: + if (!strcmp("-6", str)) { + argp->ival = 6; + } else if (!strcmp("-4", str)) { + argp->ival = 4; + } else { + fprintf(stderr, "***Version must be either 4 or 6\n"); + return 0; + } + break; + } + + return 1; +} +#endif /* !BUILD_AS_LIB */ + /* * findcmd - find a command in a command description table */ static int findcmd( - register char *str, - struct xcmd *clist1, - struct xcmd *clist2, - struct xcmd **cmd + const char * str, + struct xcmd * clist1, + struct xcmd * clist2, + struct xcmd ** cmd ) { - register struct xcmd *cl; - register int clen; + struct xcmd *cl; + int clen; int nmatch; struct xcmd *nearmatch = NULL; struct xcmd *clist; @@ -1625,159 +1799,124 @@ findcmd( /* - * getarg - interpret an argument token - */ -static int -getarg( - char *str, - int code, - arg_v *argp - ) -{ - int isneg; - char *cp, *np; - static const char *digits = "0123456789"; - - switch (code & ~OPT) { - case NTP_STR: - argp->string = str; - break; - case NTP_ADD: - if (!getnetnum(str, &(argp->netnum), (char *)0, 0)) { - return 0; - } - break; - case NTP_INT: - case NTP_UINT: - isneg = 0; - np = str; - if (*np == '&') { - np++; - isneg = atoi(np); - if (isneg <= 0) { - (void) fprintf(stderr, - "***Association value `%s' invalid/undecodable\n", str); - return 0; - } - if (isneg > numassoc) { - if (numassoc == 0) { - (void) fprintf(stderr, - "***Association for `%s' unknown (max &%d)\n", - str, numassoc); - return 0; - } else { - isneg = numassoc; - } - } - argp->uval = assoc_cache[isneg-1].assid; - break; - } - - if (*np == '-') { - np++; - isneg = 1; - } - - argp->uval = 0; - do { - cp = strchr(digits, *np); - if (cp == NULL) { - (void) fprintf(stderr, - "***Illegal integer value %s\n", str); - return 0; - } - argp->uval *= 10; - argp->uval += (cp - digits); - } while (*(++np) != '\0'); - - if (isneg) { - if ((code & ~OPT) == NTP_UINT) { - (void) fprintf(stderr, - "***Value %s should be unsigned\n", str); - return 0; - } - argp->ival = -argp->ival; - } - break; - case IP_VERSION: - if (!strcmp("-6", str)) - argp->ival = 6 ; - else if (!strcmp("-4", str)) - argp->ival = 4 ; - else { - (void) fprintf(stderr, - "***Version must be either 4 or 6\n"); - return 0; - } - break; - } - - return 1; -} - - -/* * getnetnum - given a host name, return its net number * and (optional) full name */ int getnetnum( const char *hname, - struct sockaddr_storage *num, + sockaddr_u *num, char *fullhost, int af ) { - int sockaddr_len; struct addrinfo hints, *ai = NULL; - sockaddr_len = (af == AF_INET) - ? sizeof(struct sockaddr_in) - : sizeof(struct sockaddr_in6); - memset((char *)&hints, 0, sizeof(struct addrinfo)); + ZERO(hints); hints.ai_flags = AI_CANONNAME; #ifdef AI_ADDRCONFIG hints.ai_flags |= AI_ADDRCONFIG; #endif - - /* decodenetnum works with addresses only */ + + /* + * decodenetnum only works with addresses, but handles syntax + * that getaddrinfo doesn't: [2001::1]:1234 + */ if (decodenetnum(hname, num)) { - if (fullhost != 0) { - getnameinfo((struct sockaddr *)num, sockaddr_len, - fullhost, sizeof(fullhost), NULL, 0, - NI_NUMERICHOST); - } + if (fullhost != NULL) + getnameinfo(&num->sa, SOCKLEN(num), fullhost, + LENHOSTNAME, NULL, 0, 0); return 1; } else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) { - memmove((char *)num, ai->ai_addr, ai->ai_addrlen); - if (ai->ai_canonname != 0) - (void) strcpy(fullhost, ai->ai_canonname); + INSIST(sizeof(*num) >= ai->ai_addrlen); + memcpy(num, ai->ai_addr, ai->ai_addrlen); + if (fullhost != NULL) { + if (ai->ai_canonname != NULL) + strlcpy(fullhost, ai->ai_canonname, + LENHOSTNAME); + else + getnameinfo(&num->sa, SOCKLEN(num), + fullhost, LENHOSTNAME, NULL, + 0, 0); + } + freeaddrinfo(ai); return 1; - } else { - (void) fprintf(stderr, "***Can't find host %s\n", hname); - return 0; } - /*NOTREACHED*/ + fprintf(stderr, "***Can't find host %s\n", hname); + + return 0; } + /* * nntohost - convert network number to host name. This routine enforces * the showhostnames setting. */ -char * +const char * nntohost( - struct sockaddr_storage *netnum + sockaddr_u *netnum + ) +{ + return nntohost_col(netnum, LIB_BUFLENGTH - 1, FALSE); +} + + +/* + * nntohost_col - convert network number to host name in fixed width. + * This routine enforces the showhostnames setting. + * When displaying hostnames longer than the width, + * the first part of the hostname is displayed. When + * displaying numeric addresses longer than the width, + * Such as IPv6 addresses, the caller decides whether + * the first or last of the numeric address is used. + */ +const char * +nntohost_col( + sockaddr_u * addr, + size_t width, + int preserve_lowaddrbits ) { - if (!showhostnames) - return stoa(netnum); - if ((netnum->ss_family == AF_INET) && ISREFCLOCKADR(netnum)) - return refnumtoa(netnum); - return socktohost(netnum); + const char * out; + + if (!showhostnames || SOCK_UNSPEC(addr)) { + if (preserve_lowaddrbits) + out = trunc_left(stoa(addr), width); + else + out = trunc_right(stoa(addr), width); + } else if (ISREFCLOCKADR(addr)) { + out = refnumtoa(addr); + } else { + out = trunc_right(socktohost(addr), width); + } + return out; } /* + * nntohostp() is the same as nntohost() plus a :port suffix + */ +const char * +nntohostp( + sockaddr_u *netnum + ) +{ + const char * hostn; + char * buf; + + if (!showhostnames || SOCK_UNSPEC(netnum)) + return sptoa(netnum); + else if (ISREFCLOCKADR(netnum)) + return refnumtoa(netnum); + + hostn = socktohost(netnum); + LIB_GETBUF(buf); + snprintf(buf, LIB_BUFLENGTH, "%s:%u", hostn, SRCPORT(netnum)); + + return buf; +} + +/* * rtdatetolfp - decode an RT-11 date into an l_fp */ static int @@ -1790,10 +1929,6 @@ rtdatetolfp( register int i; struct calendar cal; char buf[4]; - static const char *months[12] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; cal.yearday = 0; @@ -1826,7 +1961,7 @@ rtdatetolfp( if (*cp++ != '-') return 0; - + for (i = 0; i < 3; i++) buf[i] = *cp++; buf[3] = '\0'; @@ -1840,7 +1975,7 @@ rtdatetolfp( if (*cp++ != '-') return 0; - + if (!isdigit((int)*cp)) return 0; cal.year = (u_short)(*cp++ - '0'); @@ -1913,24 +2048,26 @@ decodets( l_fp *lfp ) { + char *cp; + char buf[30]; + size_t b; + /* * If it starts with a 0x, decode as hex. */ if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X')) - return hextolfp(str+2, lfp); + return hextolfp(str+2, lfp); /* * If it starts with a '"', try it as an RT-11 date. */ if (*str == '"') { - register char *cp = str+1; - register char *bp; - char buf[30]; - - bp = buf; - while (*cp != '"' && *cp != '\0' && bp < &buf[29]) - *bp++ = *cp++; - *bp = '\0'; + cp = str + 1; + b = 0; + while ('"' != *cp && '\0' != *cp && + b < COUNTOF(buf) - 1) + buf[b++] = *cp++; + buf[b] = '\0'; return rtdatetolfp(buf, lfp); } @@ -1939,14 +2076,15 @@ decodets( * about heuristics! */ if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f')) - return hextolfp(str, lfp); + return hextolfp(str, lfp); /* * Try it as a decimal. If this fails, try as an unquoted * RT-11 date. This code should go away eventually. */ if (atolfp(str, lfp)) - return 1; + return 1; + return rtdatetolfp(str, lfp); } @@ -1975,8 +2113,8 @@ decodeint( { if (*str == '0') { if (*(str+1) == 'x' || *(str+1) == 'X') - return hextoint(str+2, val); - return octtoint(str, val); + return hextoint(str+2, (u_long *)val); + return octtoint(str, (u_long *)val); } return atoint(str, val); } @@ -2051,61 +2189,56 @@ help( FILE *fp ) { - struct xcmd *xcp; - char *cmd; + struct xcmd *xcp = NULL; /* quiet warning */ + const char *cmd; const char *list[100]; - int word, words; - int row, rows; - int col, cols; + size_t word, words; + size_t row, rows; + size_t col, cols; + size_t length; if (pcmd->nargs == 0) { words = 0; - for (xcp = builtins; xcp->keyword != 0; xcp++) { - if (*(xcp->keyword) != '?') - list[words++] = xcp->keyword; + for (xcp = builtins; xcp->keyword != NULL; xcp++) { + if (*(xcp->keyword) != '?' && + words < COUNTOF(list)) + list[words++] = xcp->keyword; } - for (xcp = opcmds; xcp->keyword != 0; xcp++) - list[words++] = xcp->keyword; + for (xcp = opcmds; xcp->keyword != NULL; xcp++) + if (words < COUNTOF(list)) + list[words++] = xcp->keyword; - qsort( -#ifdef QSORT_USES_VOID_P - (void *) -#else - (char *) -#endif - (list), (size_t)(words), sizeof(char *), helpsort); + qsort((void *)list, words, sizeof(list[0]), helpsort); col = 0; for (word = 0; word < words; word++) { - int length = strlen(list[word]); - if (col < length) { - col = length; - } + length = strlen(list[word]); + col = max(col, length); } cols = SCREENWIDTH / ++col; - rows = (words + cols - 1) / cols; + rows = (words + cols - 1) / cols; - (void) fprintf(fp, "ntpq commands:\n"); + fprintf(fp, "ntpq commands:\n"); for (row = 0; row < rows; row++) { - for (word = row; word < words; word += rows) { - (void) fprintf(fp, "%-*.*s", col, col-1, list[word]); - } - (void) fprintf(fp, "\n"); - } + for (word = row; word < words; word += rows) + fprintf(fp, "%-*.*s", (int)col, + (int)col - 1, list[word]); + fprintf(fp, "\n"); + } } else { cmd = pcmd->argval[0].string; words = findcmd(cmd, builtins, opcmds, &xcp); if (words == 0) { - (void) fprintf(stderr, - "Command `%s' is unknown\n", cmd); + fprintf(stderr, + "Command `%s' is unknown\n", cmd); return; } else if (words >= 2) { - (void) fprintf(stderr, - "Command `%s' is ambiguous\n", cmd); + fprintf(stderr, + "Command `%s' is ambiguous\n", cmd); return; } - (void) fprintf(fp, "function: %s\n", xcp->comment); + fprintf(fp, "function: %s\n", xcp->comment); printusage(xcp, fp); } } @@ -2114,29 +2247,18 @@ help( /* * helpsort - do hostname qsort comparisons */ -#ifdef QSORT_USES_VOID_P static int helpsort( const void *t1, const void *t2 ) { - char const * const * name1 = (char const * const *)t1; - char const * const * name2 = (char const * const *)t2; + const char * const * name1 = t1; + const char * const * name2 = t2; return strcmp(*name1, *name2); } -#else -static int -helpsort( - char **name1, - char **name2 - ) -{ - return strcmp(*name1, *name2); -} -#endif /* * printusage - print usage information for a command @@ -2149,6 +2271,8 @@ printusage( { register int i; + /* XXX: Do we need to warn about extra args here too? */ + (void) fprintf(fp, "usage: %s", xcp->keyword); for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) { if (xcp->arg[i] & OPT) @@ -2172,11 +2296,11 @@ timeout( int val; if (pcmd->nargs == 0) { - val = tvout.tv_sec * 1000 + tvout.tv_usec / 1000; + val = (int)tvout.tv_sec * 1000 + tvout.tv_usec / 1000; (void) fprintf(fp, "primary timeout %d ms\n", val); } else { tvout.tv_sec = pcmd->argval[0].uval / 1000; - tvout.tv_usec = (pcmd->argval[0].uval - (tvout.tv_sec * 1000)) + tvout.tv_usec = (pcmd->argval[0].uval - ((long)tvout.tv_sec * 1000)) * 1000; } } @@ -2229,9 +2353,10 @@ host( if (pcmd->nargs == 0) { if (havehost) - (void) fprintf(fp, "current host is %s\n", currenthost); + (void) fprintf(fp, "current host is %s\n", + currenthost); else - (void) fprintf(fp, "no current host\n"); + (void) fprintf(fp, "no current host\n"); return; } @@ -2242,25 +2367,19 @@ host( ai_fam_templ = AF_INET; else if (!strcmp("-6", pcmd->argval[i].string)) ai_fam_templ = AF_INET6; - else { - if (havehost) - (void) fprintf(fp, - "current host remains %s\n", currenthost); - else - (void) fprintf(fp, "still no current host\n"); - return; - } + else + goto no_change; i = 1; } - if (openhost(pcmd->argval[i].string)) { - (void) fprintf(fp, "current host set to %s\n", currenthost); - numassoc = 0; + if (openhost(pcmd->argval[i].string, ai_fam_templ)) { + fprintf(fp, "current host set to %s\n", currenthost); } else { + no_change: if (havehost) - (void) fprintf(fp, - "current host remains %s\n", currenthost); + fprintf(fp, "current host remains %s\n", + currenthost); else - (void) fprintf(fp, "still no current host\n"); + fprintf(fp, "still no current host\n"); } } @@ -2310,21 +2429,34 @@ keytype( FILE *fp ) { - if (pcmd->nargs == 0) - fprintf(fp, "keytype is %s\n", - (info_auth_keytype == KEY_TYPE_MD5) ? "MD5" : "???"); - else - switch (*(pcmd->argval[0].string)) { - case 'm': - case 'M': - info_auth_keytype = KEY_TYPE_MD5; - break; + const char * digest_name; + size_t digest_len; + int key_type; + + if (!pcmd->nargs) { + fprintf(fp, "keytype is %s with %lu octet digests\n", + keytype_name(info_auth_keytype), + (u_long)info_auth_hashlen); + return; + } - default: - fprintf(fp, "keytype must be 'md5'\n"); - } -} + digest_name = pcmd->argval[0].string; + digest_len = 0; + key_type = keytype_from_text(digest_name, &digest_len); + + if (!key_type) { + fprintf(fp, "keytype is not valid. " +#ifdef OPENSSL + "Type \"help keytype\" for the available digest types.\n"); +#else + "Only \"md5\" is available.\n"); +#endif + return; + } + info_auth_keytype = key_type; + info_auth_hashlen = digest_len; +} /* @@ -2337,23 +2469,27 @@ passwd( FILE *fp ) { - char *pass; + const char *pass; if (info_auth_keyid == 0) { - int u_keyid = getkeyid("Keyid: "); - if (u_keyid == 0 || u_keyid > NTP_MAXKEY) { - (void)fprintf(fp, "Invalid key identifier\n"); + info_auth_keyid = getkeyid("Keyid: "); + if (info_auth_keyid == 0) { + (void)fprintf(fp, "Keyid must be defined\n"); return; } - info_auth_keyid = u_keyid; } - pass = getpass("MD5 Password: "); - if (*pass == '\0') - (void) fprintf(fp, "Password unchanged\n"); + if (pcmd->nargs >= 1) + pass = pcmd->argval[0].string; else { - authusekey(info_auth_keyid, info_auth_keytype, (u_char *)pass); - authtrust(info_auth_keyid, 1); + pass = getpass_keytype(info_auth_keytype); + if ('\0' == pass[0]) { + fprintf(fp, "Password unchanged\n"); + return; + } } + authusekey(info_auth_keyid, info_auth_keytype, + (const u_char *)pass); + authtrust(info_auth_keyid, 1); } @@ -2523,37 +2659,46 @@ ntpversion( } +static void __attribute__((__format__(__printf__, 1, 0))) +vwarning(const char *fmt, va_list ap) +{ + int serrno = errno; + (void) fprintf(stderr, "%s: ", progname); + vfprintf(stderr, fmt, ap); + (void) fprintf(stderr, ": %s", strerror(serrno)); +} + /* * warning - print a warning message */ -static void +static void __attribute__((__format__(__printf__, 1, 2))) warning( const char *fmt, - const char *st1, - const char *st2 + ... ) { - (void) fprintf(stderr, "%s: ", progname); - (void) fprintf(stderr, fmt, st1, st2); - (void) fprintf(stderr, ": "); - perror(""); + va_list ap; + va_start(ap, fmt); + vwarning(fmt, ap); + va_end(ap); } /* * error - print a message and exit */ -static void +static void __attribute__((__format__(__printf__, 1, 2))) error( const char *fmt, - const char *st1, - const char *st2 + ... ) { - warning(fmt, st1, st2); + va_list ap; + va_start(ap, fmt); + vwarning(fmt, ap); + va_end(ap); exit(1); } - /* * getkeyid - prompt the user for a keyid to use */ @@ -2562,29 +2707,28 @@ getkeyid( const char *keyprompt ) { - register char *p; - register int c; + int c; FILE *fi; char pbuf[20]; + size_t i; + size_t ilim; #ifndef SYS_WINNT if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL) #else - if ((fi = _fdopen((int)GetStdHandle(STD_INPUT_HANDLE), "r")) == NULL) + if ((fi = _fdopen(open("CONIN$", _O_TEXT), "r")) == NULL) #endif /* SYS_WINNT */ fi = stdin; - else + else setbuf(fi, (char *)NULL); fprintf(stderr, "%s", keyprompt); fflush(stderr); - for (p=pbuf; (c = getc(fi))!='\n' && c!=EOF;) { - if (p < &pbuf[18]) - *p++ = (char)c; - } - *p = '\0'; + for (i = 0, ilim = COUNTOF(pbuf) - 1; + i < ilim && (c = getc(fi)) != '\n' && c != EOF; + ) + pbuf[i++] = (char)c; + pbuf[i] = '\0'; if (fi != stdin) - fclose(fi); - if (strcmp(pbuf, "0") == 0) - return 0; + fclose(fi); return (u_long) atoi(pbuf); } @@ -2596,82 +2740,93 @@ getkeyid( */ static void atoascii( - int length, - char *data, - char *outdata + const char *in, + size_t in_octets, + char *out, + size_t out_octets ) { - register u_char *cp; - register u_char *ocp; - register u_char c; - - if (!data) - { - *outdata = '\0'; + const u_char * pchIn; + const u_char * pchInLimit; + u_char * pchOut; + u_char c; + + pchIn = (const u_char *)in; + pchInLimit = pchIn + in_octets; + pchOut = (u_char *)out; + + if (NULL == pchIn) { + if (0 < out_octets) + *pchOut = '\0'; return; } - ocp = (u_char *)outdata; - for (cp = (u_char *)data; cp < (u_char *)data + length; cp++) { - c = *cp; - if (c == '\0') - break; - if (c == '\0') - break; - if (c > 0177) { - *ocp++ = 'M'; - *ocp++ = '-'; - c &= 0177; - } +#define ONEOUT(c) \ +do { \ + if (0 == --out_octets) { \ + *pchOut = '\0'; \ + return; \ + } \ + *pchOut++ = (c); \ +} while (0) - if (c < ' ') { - *ocp++ = '^'; - *ocp++ = (u_char)(c + '@'); - } else if (c == 0177) { - *ocp++ = '^'; - *ocp++ = '?'; - } else { - *ocp++ = c; + for ( ; pchIn < pchInLimit; pchIn++) { + c = *pchIn; + if ('\0' == c) + break; + if (c & 0x80) { + ONEOUT('M'); + ONEOUT('-'); + c &= 0x7f; } - if (ocp >= ((u_char *)outdata + length - 4)) - break; + if (c < ' ') { + ONEOUT('^'); + ONEOUT((u_char)(c + '@')); + } else if (0x7f == c) { + ONEOUT('^'); + ONEOUT('?'); + } else + ONEOUT(c); } - *ocp++ = '\0'; -} + ONEOUT('\0'); +#undef ONEOUT +} /* * makeascii - print possibly ascii data using the character * transformations that cat -v uses. */ -static void +void makeascii( int length, - char *data, + const char *data, FILE *fp ) { - register u_char *cp; - register int c; + const u_char *data_u_char; + const u_char *cp; + int c; - for (cp = (u_char *)data; cp < (u_char *)data + length; cp++) { + data_u_char = (const u_char *)data; + + for (cp = data_u_char; cp < data_u_char + length; cp++) { c = (int)*cp; - if (c > 0177) { + if (c & 0x80) { putc('M', fp); putc('-', fp); - c &= 0177; + c &= 0x7f; } if (c < ' ') { putc('^', fp); - putc(c+'@', fp); - } else if (c == 0177) { + putc(c + '@', fp); + } else if (0x7f == c) { putc('^', fp); putc('?', fp); - } else { + } else putc(c, fp); - } } } @@ -2692,6 +2847,62 @@ asciize( /* + * truncate string to fit clipping excess at end. + * "too long" -> "too l" + * Used for hostnames. + */ +const char * +trunc_right( + const char * src, + size_t width + ) +{ + size_t sl; + char * out; + + + sl = strlen(src); + if (sl > width && LIB_BUFLENGTH - 1 > width && width > 0) { + LIB_GETBUF(out); + memcpy(out, src, width); + out[width] = '\0'; + + return out; + } + + return src; +} + + +/* + * truncate string to fit by preserving right side and using '_' to hint + * "too long" -> "_long" + * Used for local IPv6 addresses, where low bits differentiate. + */ +const char * +trunc_left( + const char * src, + size_t width + ) +{ + size_t sl; + char * out; + + + sl = strlen(src); + if (sl > width && LIB_BUFLENGTH - 1 > width && width > 1) { + LIB_GETBUF(out); + out[0] = '_'; + memcpy(&out[1], &src[sl + 1 - width], width); + + return out; + } + + return src; +} + + +/* * Some circular buffer space */ #define CBLEN 80 @@ -2706,16 +2917,16 @@ int nextcb = 0; int nextvar( int *datalen, - char **datap, + const char **datap, char **vname, char **vvalue ) { - register char *cp; - register char *np; - register char *cpend; - register char *npend; /* character after last */ - int quoted = 0; + const char *cp; + const char *np; + const char *cpend; + size_t srclen; + size_t len; static char name[MAXVARLEN]; static char value[MAXVALLEN]; @@ -2726,40 +2937,34 @@ nextvar( * Space past commas and white space */ while (cp < cpend && (*cp == ',' || isspace((int)*cp))) - cp++; - if (cp == cpend) - return 0; - + cp++; + if (cp >= cpend) + return 0; + /* * Copy name until we hit a ',', an '=', a '\r' or a '\n'. Backspace * over any white space and terminate it. */ - np = name; - npend = &name[MAXVARLEN]; - while (cp < cpend && np < npend && *cp != ',' && *cp != '=' - && *cp != '\r' && *cp != '\n') - *np++ = *cp++; - /* - * Check if we ran out of name space, without reaching the end or a - * terminating character - */ - if (np == npend && !(cp == cpend || *cp == ',' || *cp == '=' || - *cp == '\r' || *cp == '\n')) - return 0; - while (isspace((int)(*(np-1)))) - np--; - *np = '\0'; + srclen = strcspn(cp, ",=\r\n"); + srclen = min(srclen, (size_t)(cpend - cp)); + len = srclen; + while (len > 0 && isspace((unsigned char)cp[len - 1])) + len--; + if (len > 0) + memcpy(name, cp, len); + name[len] = '\0'; *vname = name; + cp += srclen; /* * Check if we hit the end of the buffer or a ','. If so we are done. */ - if (cp == cpend || *cp == ',' || *cp == '\r' || *cp == '\n') { - if (cp != cpend) - cp++; + if (cp >= cpend || *cp == ',' || *cp == '\r' || *cp == '\n') { + if (cp < cpend) + cp++; *datap = cp; *datalen = cpend - cp; - *vvalue = (char *)0; + *vvalue = NULL; return 1; } @@ -2767,68 +2972,54 @@ nextvar( * So far, so good. Copy out the value */ cp++; /* past '=' */ - while (cp < cpend && (isspace((int)*cp) && *cp != '\r' && *cp != '\n')) - cp++; - np = value; - npend = &value[MAXVALLEN]; - while (cp < cpend && np < npend && ((*cp != ',') || quoted)) - { - quoted ^= ((*np++ = *cp++) == '"'); + while (cp < cpend && (isspace((unsigned char)*cp) && *cp != '\r' && *cp != '\n')) + cp++; + np = cp; + if ('"' == *np) { + do { + np++; + } while (np < cpend && '"' != *np); + if (np < cpend && '"' == *np) + np++; + } else { + while (np < cpend && ',' != *np && '\r' != *np) + np++; } - - /* - * Check if we overran the value buffer while still in a quoted string - * or without finding a comma - */ - if (np == npend && (quoted || *cp != ',')) - return 0; + len = np - cp; + if (np > cpend || len >= sizeof(value) || + (np < cpend && ',' != *np && '\r' != *np)) + return 0; + memcpy(value, cp, len); /* * Trim off any trailing whitespace */ - while (np > value && isspace((int)(*(np-1)))) - np--; - *np = '\0'; + while (len > 0 && isspace((unsigned char)value[len - 1])) + len--; + value[len] = '\0'; /* * Return this. All done. */ - if (cp != cpend) - cp++; - *datap = cp; - *datalen = cpend - cp; + if (np < cpend && ',' == *np) + np++; + *datap = np; + *datalen = cpend - np; *vvalue = value; return 1; } -/* - * findvar - see if this variable is known to us. - * If "code" is 1, return ctl_var->code. - * Otherwise return the ordinal position of the found variable. - */ -int -findvar( - char *varname, - struct ctl_var *varlist, - int code - ) +u_short +varfmt(const char * varname) { - register char *np; - register struct ctl_var *vl; + u_int n; - vl = varlist; - np = varname; - while (vl->fmt != EOV) { - if (vl->fmt != PADDING && STREQ(np, vl->text)) - return (code) - ? vl->code - : (vl - varlist) - ; - vl++; - } - return 0; -} + for (n = 0; n < COUNTOF(cookedvars); n++) + if (!strcmp(varname, cookedvars[n].varname)) + return cookedvars[n].fmt; + return PADDING; +} /* @@ -2837,16 +3028,17 @@ findvar( void printvars( int length, - char *data, + const char *data, int status, int sttype, + int quiet, FILE *fp ) { if (rawmode) - rawprint(sttype, length, data, status, fp); + rawprint(sttype, length, data, status, quiet, fp); else - cookedprint(sttype, length, data, status, fp); + cookedprint(sttype, length, data, status, quiet, fp); } @@ -2857,13 +3049,14 @@ static void rawprint( int datatype, int length, - char *data, + const char *data, int status, + int quiet, FILE *fp ) { - register char *cp; - register char *cpend; + const char *cp; + const char *cpend; /* * Essentially print the data as is. We reformat unprintables, though. @@ -2871,7 +3064,8 @@ rawprint( cp = data; cpend = data + length; - (void) fprintf(fp, "status=0x%04x,\n", status); + if (!quiet) + (void) fprintf(fp, "status=0x%04x,\n", status); while (cp < cpend) { if (*cp == '\r') { @@ -2880,13 +3074,12 @@ rawprint( * \n, supress this, else pretty print it. Otherwise * just output the character. */ - if (cp == (cpend-1) || *(cp+1) != '\n') + if (cp == (cpend - 1) || *(cp + 1) != '\n') makeascii(1, cp, fp); - } else if (isspace((int)*cp) || isprint((int)*cp)) { + } else if (isspace((unsigned char)*cp) || isprint((unsigned char)*cp)) putc(*cp, fp); - } else { + else makeascii(1, cp, fp); - } cp++; } } @@ -2916,36 +3109,31 @@ startoutput(void) static void output( FILE *fp, - char *name, - char *value + const char *name, + const char *value ) { - int lenname; - int lenvalue; + size_t len; - lenname = strlen(name); - lenvalue = strlen(value); + /* strlen of "name=value" */ + len = strlen(name) + 1 + strlen(value); if (out_chars != 0) { - putc(',', fp); - out_chars++; - out_linecount++; - if ((out_linecount + lenname + lenvalue + 3) > MAXOUTLINE) { - putc('\n', fp); - out_chars++; + out_chars += 2; + if ((out_linecount + len + 2) > MAXOUTLINE) { + fputs(",\n", fp); out_linecount = 0; } else { - putc(' ', fp); - out_chars++; - out_linecount++; + fputs(", ", fp); + out_linecount += 2; } } fputs(name, fp); putc('=', fp); fputs(value, fp); - out_chars += lenname + 1 + lenvalue; - out_linecount += lenname + 1 + lenvalue; + out_chars += len; + out_linecount += len; } @@ -2958,7 +3146,7 @@ endoutput( ) { if (out_chars != 0) - putc('\n', fp); + putc('\n', fp); } @@ -2985,7 +3173,7 @@ outputarr( */ for (i = (int)strlen(name); i < 11; i++) *bp++ = ' '; - + for (i = narr; i > 0; i--) { if (i != narr) *bp++ = ' '; @@ -3012,33 +3200,44 @@ tstflags( u_long val ) { - register char *cb, *s; + register char *cp, *s; + size_t cb; register int i; register const char *sep; sep = ""; i = 0; - s = cb = &circ_buf[nextcb][0]; + s = cp = circ_buf[nextcb]; if (++nextcb >= NUMCB) - nextcb = 0; + nextcb = 0; + cb = sizeof(circ_buf[0]); - sprintf(cb, "%02lx", val); - cb += strlen(cb); + snprintf(cp, cb, "%02lx", val); + cp += strlen(cp); + cb -= strlen(cp); if (!val) { - strcat(cb, " ok"); - cb += strlen(cb); + strlcat(cp, " ok", cb); + cp += strlen(cp); + cb -= strlen(cp); } else { - *cb++ = ' '; - for (i = 0; i < 13; i++) { + if (cb) { + *cp++ = ' '; + cb--; + } + for (i = 0; i < (int)COUNTOF(tstflagnames); i++) { if (val & 0x1) { - sprintf(cb, "%s%s", sep, tstflagnames[i]); + snprintf(cp, cb, "%s%s", sep, + tstflagnames[i]); sep = ", "; - cb += strlen(cb); + cp += strlen(cp); + cb -= strlen(cp); } val >>= 1; } } - *cb = '\0'; + if (cb) + *cp = '\0'; + return s; } @@ -3049,185 +3248,122 @@ static void cookedprint( int datatype, int length, - char *data, + const char *data, int status, + int quiet, FILE *fp ) { - register int varid; char *name; char *value; char output_raw; int fmt; - struct ctl_var *varlist; l_fp lfp; - long ival; - struct sockaddr_storage hval; + sockaddr_u hval; u_long uval; - l_fp lfparr[8]; int narr; + size_t len; + l_fp lfparr[8]; + char b[12]; + char bn[2 * MAXVARLEN]; + char bv[2 * MAXVALLEN]; - switch (datatype) { - case TYPE_PEER: - varlist = peer_var; - break; - case TYPE_SYS: - varlist = sys_var; - break; - case TYPE_CLOCK: - varlist = clock_var; - break; - default: - (void) fprintf(stderr, "Unknown datatype(0x%x) in cookedprint\n", datatype); - return; - } + UNUSED_ARG(datatype); - (void) fprintf(fp, "status=%04x %s,\n", status, - statustoa(datatype, status)); + if (!quiet) + fprintf(fp, "status=%04x %s,\n", status, + statustoa(datatype, status)); startoutput(); while (nextvar(&length, &data, &name, &value)) { - varid = findvar(name, varlist, 0); - if (varid == 0) { + fmt = varfmt(name); + output_raw = 0; + switch (fmt) { + + case PADDING: output_raw = '*'; - } else { - output_raw = 0; - fmt = varlist[varid].fmt; - switch(fmt) { - case TS: - if (!decodets(value, &lfp)) - output_raw = '?'; - else - output(fp, name, prettydate(&lfp)); - break; - case FL: - case FU: - case FS: - if (!decodetime(value, &lfp)) - output_raw = '?'; - else { - switch (fmt) { - case FL: - output(fp, name, - lfptoms(&lfp, 3)); - break; - case FU: - output(fp, name, - ulfptoms(&lfp, 3)); - break; - case FS: - output(fp, name, - lfptoms(&lfp, 3)); - break; - } - } - break; - - case UI: - if (!decodeuint(value, &uval)) - output_raw = '?'; - else - output(fp, name, uinttoa(uval)); - break; - - case SI: - if (!decodeint(value, &ival)) - output_raw = '?'; - else - output(fp, name, inttoa(ival)); - break; - - case HA: - case NA: - if (!decodenetnum(value, &hval)) - output_raw = '?'; - else if (fmt == HA){ - output(fp, name, nntohost(&hval)); - } else { - output(fp, name, stoa(&hval)); - } - break; - - case ST: - output_raw = '*'; - break; - - case RF: - if (decodenetnum(value, &hval)) { - if ((hval.ss_family == AF_INET) && - ISREFCLOCKADR(&hval)) - output(fp, name, - refnumtoa(&hval)); - else - output(fp, name, stoa(&hval)); - } else if ((int)strlen(value) <= 4) - output(fp, name, value); - else - output_raw = '?'; - break; - - case LP: - if (!decodeuint(value, &uval) || uval > 3) - output_raw = '?'; - else { - char b[3]; - b[0] = b[1] = '0'; - if (uval & 0x2) - b[0] = '1'; - if (uval & 0x1) - b[1] = '1'; - b[2] = '\0'; - output(fp, name, b); - } - break; + break; - case OC: - if (!decodeuint(value, &uval)) - output_raw = '?'; - else { - char b[12]; + case TS: + if (!decodets(value, &lfp)) + output_raw = '?'; + else + output(fp, name, prettydate(&lfp)); + break; - (void) snprintf(b, sizeof b, "%03lo", uval); - output(fp, name, b); - } - break; - - case MD: - if (!decodeuint(value, &uval)) - output_raw = '?'; - else - output(fp, name, uinttoa(uval)); - break; - - case AR: - if (!decodearr(value, &narr, lfparr)) - output_raw = '?'; - else - outputarr(fp, name, narr, lfparr); - break; + case HA: /* fallthru */ + case NA: + if (!decodenetnum(value, &hval)) { + output_raw = '?'; + } else if (fmt == HA){ + output(fp, name, nntohost(&hval)); + } else { + output(fp, name, stoa(&hval)); + } + break; - case FX: - if (!decodeuint(value, &uval)) - output_raw = '?'; + case RF: + if (decodenetnum(value, &hval)) { + if (ISREFCLOCKADR(&hval)) + output(fp, name, + refnumtoa(&hval)); else - output(fp, name, tstflags(uval)); - break; - - default: - (void) fprintf(stderr, - "Internal error in cookedprint, %s=%s, fmt %d\n", - name, value, fmt); - break; + output(fp, name, stoa(&hval)); + } else if (strlen(value) <= 4) { + output(fp, name, value); + } else { + output_raw = '?'; + } + break; + + case LP: + if (!decodeuint(value, &uval) || uval > 3) { + output_raw = '?'; + } else { + b[0] = (0x2 & uval) + ? '1' + : '0'; + b[1] = (0x1 & uval) + ? '1' + : '0'; + b[2] = '\0'; + output(fp, name, b); + } + break; + + case OC: + if (!decodeuint(value, &uval)) { + output_raw = '?'; + } else { + snprintf(b, sizeof(b), "%03lo", uval); + output(fp, name, b); } + break; + + case AR: + if (!decodearr(value, &narr, lfparr)) + output_raw = '?'; + else + outputarr(fp, name, narr, lfparr); + break; + case FX: + if (!decodeuint(value, &uval)) + output_raw = '?'; + else + output(fp, name, tstflags(uval)); + break; + + default: + fprintf(stderr, "Internal error in cookedprint, %s=%s, fmt %d\n", + name, value, fmt); + output_raw = '?'; + break; } - if (output_raw != 0) { - char bn[401]; - char bv[401]; - int len; - atoascii(400, name, bn); - atoascii(400, value, bv); + if (output_raw != 0) { + atoascii(name, MAXVARLEN, bn, sizeof(bn)); + atoascii(value, MAXVALLEN, bv, sizeof(bv)); if (output_raw != '*') { len = strlen(bv); bv[len] = output_raw; @@ -3247,47 +3383,182 @@ void sortassoc(void) { if (numassoc > 1) - qsort( -#ifdef QSORT_USES_VOID_P - (void *) -#else - (char *) -#endif - assoc_cache, (size_t)numassoc, - sizeof(struct association), assoccmp); + qsort(assoc_cache, (size_t)numassoc, + sizeof(assoc_cache[0]), &assoccmp); } /* * assoccmp - compare two associations */ -#ifdef QSORT_USES_VOID_P static int assoccmp( const void *t1, const void *t2 ) { - const struct association *ass1 = (const struct association *)t1; - const struct association *ass2 = (const struct association *)t2; + const struct association *ass1 = t1; + const struct association *ass2 = t2; if (ass1->assid < ass2->assid) - return -1; + return -1; if (ass1->assid > ass2->assid) - return 1; + return 1; return 0; } -#else -static int -assoccmp( - struct association *ass1, - struct association *ass2 + + +/* + * grow_assoc_cache() - enlarge dynamic assoc_cache array + * + * The strategy is to add an assumed 4k page size at a time, leaving + * room for malloc() bookkeeping overhead equivalent to 4 pointers. + */ +void +grow_assoc_cache(void) +{ + static size_t prior_sz; + size_t new_sz; + + new_sz = prior_sz + 4 * 1024; + if (0 == prior_sz) { + new_sz -= 4 * sizeof(void *); + } + assoc_cache = erealloc_zero(assoc_cache, new_sz, prior_sz); + prior_sz = new_sz; + assoc_cache_slots = new_sz / sizeof(assoc_cache[0]); +} + + +/* + * ntpq_custom_opt_handler - autoopts handler for -c and -p + * + * By default, autoopts loses the relative order of -c and -p options + * on the command line. This routine replaces the default handler for + * those routines and builds a list of commands to execute preserving + * the order. + */ +void +ntpq_custom_opt_handler( + tOptions *pOptions, + tOptDesc *pOptDesc ) { - if (ass1->assid < ass2->assid) - return -1; - if (ass1->assid > ass2->assid) - return 1; - return 0; + switch (pOptDesc->optValue) { + + default: + fprintf(stderr, + "ntpq_custom_opt_handler unexpected option '%c' (%d)\n", + pOptDesc->optValue, pOptDesc->optValue); + exit(1); + + case 'c': + ADDCMD(pOptDesc->pzLastArg); + break; + + case 'p': + ADDCMD("peers"); + break; + } +} +/* + * Obtain list of digest names + */ + +#ifdef OPENSSL +# ifdef HAVE_EVP_MD_DO_ALL_SORTED +struct hstate { + char *list; + const char **seen; + int idx; +}; +#define K_PER_LINE 8 +#define K_NL_PFX_STR "\n " +#define K_DELIM_STR ", " +static void list_md_fn(const EVP_MD *m, const char *from, const char *to, void *arg ) +{ + size_t len, n; + const char *name, *cp, **seen; + struct hstate *hstate = arg; + EVP_MD_CTX ctx; + u_int digest_len; + u_char digest[EVP_MAX_MD_SIZE]; + + if (!m) + return; /* Ignore aliases */ + + name = EVP_MD_name(m); + + /* Lowercase names aren't accepted by keytype_from_text in ssl_init.c */ + + for( cp = name; *cp; cp++ ) { + if( islower(*cp) ) + return; + } + len = (cp - name) + 1; + + /* There are duplicates. Discard if name has been seen. */ + + for (seen = hstate->seen; *seen; seen++) + if (!strcmp(*seen, name)) + return; + n = (seen - hstate->seen) + 2; + hstate->seen = realloc(hstate->seen, n * sizeof(*seen)); + hstate->seen[n-2] = name; + hstate->seen[n-1] = NULL; + + /* Discard MACs that NTP won't accept. + * Keep this consistent with keytype_from_text() in ssl_init.c. + */ + + EVP_DigestInit(&ctx, EVP_get_digestbyname(name)); + EVP_DigestFinal(&ctx, digest, &digest_len); + if (digest_len > (MAX_MAC_LEN - sizeof(keyid_t))) + return; + + if (hstate->list != NULL) + len += strlen(hstate->list); + len += (hstate->idx >= K_PER_LINE)? strlen(K_NL_PFX_STR): strlen(K_DELIM_STR); + + if (hstate->list == NULL) { + hstate->list = (char *)malloc(len); + hstate->list[0] = '\0'; + } else + hstate->list = (char *)realloc(hstate->list, len); + + sprintf(hstate->list + strlen(hstate->list), "%s%s", + ((hstate->idx >= K_PER_LINE)? K_NL_PFX_STR : K_DELIM_STR), + name); + if (hstate->idx >= K_PER_LINE) + hstate->idx = 1; + else + hstate->idx++; +} +# endif +#endif + +static char *list_digest_names(void) +{ + char *list = NULL; + +#ifdef OPENSSL +# ifdef HAVE_EVP_MD_DO_ALL_SORTED + struct hstate hstate = { NULL, NULL, K_PER_LINE+1 }; + + hstate.seen = (const char **)calloc(1, sizeof( const char * )); + + INIT_SSL(); + EVP_MD_do_all_sorted(list_md_fn, &hstate); + list = hstate.list; + free(hstate.seen); +# else + list = (char *)malloc(sizeof("md5, others (upgrade to OpenSSL-1.0 for full list)")); + strcpy(list, "md5, others (upgrade to OpenSSL-1.0 for full list)"); +# endif +#else + list = (char *)malloc(sizeof("md5")); + strcpy(list, "md5"); +#endif + + return list; } -#endif /* not QSORT_USES_VOID_P */ |