]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - contrib/ntp/ntpq/ntpq.c
Fix multiple vulnerabilities in ntp. [SA-18:02.ntp]
[FreeBSD/releng/10.3.git] / contrib / ntp / ntpq / ntpq.c
1 /*
2  * ntpq - query an NTP server using mode 6 commands
3  */
4 #include <config.h>
5 #include <ctype.h>
6 #include <signal.h>
7 #include <setjmp.h>
8 #include <stddef.h>
9 #include <stdio.h>
10 #include <sys/types.h>
11 #include <sys/time.h>
12 #ifdef HAVE_UNISTD_H
13 # include <unistd.h>
14 #endif
15 #ifdef HAVE_FCNTL_H
16 # include <fcntl.h>
17 #endif
18 #ifdef SYS_WINNT
19 # include <mswsock.h>
20 #endif
21 #include <isc/net.h>
22 #include <isc/result.h>
23
24 #include "ntpq.h"
25 #include "ntp_assert.h"
26 #include "ntp_stdlib.h"
27 #include "ntp_unixtime.h"
28 #include "ntp_calendar.h"
29 #include "ntp_select.h"
30 #include "ntp_assert.h"
31 #include "lib_strbuf.h"
32 #include "ntp_lineedit.h"
33 #include "ntp_debug.h"
34 #ifdef OPENSSL
35 #include "openssl/evp.h"
36 #include "openssl/objects.h"
37 #include "openssl/err.h"
38 #ifdef SYS_WINNT
39 # include "openssl/opensslv.h"
40 # if !defined(HAVE_EVP_MD_DO_ALL_SORTED) && OPENSSL_VERSION_NUMBER > 0x10000000L
41 #    define HAVE_EVP_MD_DO_ALL_SORTED   1
42 # endif
43 #endif
44 #include "libssl_compat.h"
45
46 #define CMAC "AES128CMAC"
47 #endif
48 #include <ssl_applink.c>
49
50 #include "ntp_libopts.h"
51 #include "safecast.h"
52
53 #ifdef SYS_VXWORKS              /* vxWorks needs mode flag -casey*/
54 # define open(name, flags)   open(name, flags, 0777)
55 # define SERVER_PORT_NUM     123
56 #endif
57
58 /* we use COMMAND as an autogen keyword */
59 #ifdef COMMAND
60 # undef COMMAND
61 #endif
62
63 /*
64  * Because we potentially understand a lot of commands we will run
65  * interactive if connected to a terminal.
66  */
67 int interactive = 0;            /* set to 1 when we should prompt */
68 const char *prompt = "ntpq> ";  /* prompt to ask him about */
69
70 /*
71  * use old readvars behavior?  --old-rv processing in ntpq resets
72  * this value based on the presence or absence of --old-rv.  It is
73  * initialized to 1 here to maintain backward compatibility with
74  * libntpq clients such as ntpsnmpd, which are free to reset it as
75  * desired.
76  */
77 int     old_rv = 1;
78
79 /*
80  * How should we display the refid?
81  * REFID_HASH, REFID_IPV4
82  */
83 te_Refid drefid = -1;
84
85 /*
86  * for get_systime()
87  */
88 s_char  sys_precision;          /* local clock precision (log2 s) */
89
90 /*
91  * Keyid used for authenticated requests.  Obtained on the fly.
92  */
93 u_long info_auth_keyid = 0;
94
95 static  int     info_auth_keytype = NID_md5;    /* MD5 */
96 static  size_t  info_auth_hashlen = 16;         /* MD5 */
97 u_long  current_time;           /* needed by authkeys; not used */
98
99 /*
100  * Flag which indicates we should always send authenticated requests
101  */
102 int always_auth = 0;
103
104 /*
105  * Flag which indicates raw mode output.
106  */
107 int rawmode = 0;
108
109 /*
110  * Packet version number we use
111  */
112 u_char pktversion = NTP_OLDVERSION + 1;
113
114 /*
115  * Don't jump if no set jmp.
116  */
117 volatile int jump = 0;
118
119 /*
120  * Format values
121  */
122 #define PADDING 0
123 #define HA      1       /* host address */
124 #define NA      2       /* network address */
125 #define LP      3       /* leap (print in binary) */
126 #define RF      4       /* refid (sometimes string, sometimes not) */
127 #define AR      5       /* array of times */
128 #define FX      6       /* test flags */
129 #define TS      7       /* l_fp timestamp in hex */
130 #define OC      8       /* integer, print in octal */
131 #define EOV     255     /* end of table */
132
133 /*
134  * For the most part ntpq simply displays what ntpd provides in the
135  * mostly plain-text mode 6 responses.  A few variable names are by
136  * default "cooked" to provide more human-friendly output.
137  */
138 const var_format cookedvars[] = {
139         { "leap",               LP },
140         { "reach",              OC },
141         { "refid",              RF },
142         { "reftime",            TS },
143         { "clock",              TS },
144         { "org",                TS },
145         { "rec",                TS },
146         { "xmt",                TS },
147         { "flash",              FX },
148         { "srcadr",             HA },
149         { "peeradr",            HA },   /* compat with others */
150         { "dstadr",             NA },
151         { "filtdelay",          AR },
152         { "filtoffset",         AR },
153         { "filtdisp",           AR },
154         { "filterror",          AR },   /* compat with others */
155 };
156
157
158
159 /*
160  * flasher bits
161  */
162 static const char *tstflagnames[] = {
163         "pkt_dup",              /* TEST1 */
164         "pkt_bogus",            /* TEST2 */
165         "pkt_unsync",           /* TEST3 */
166         "pkt_denied",           /* TEST4 */
167         "pkt_auth",             /* TEST5 */
168         "pkt_stratum",          /* TEST6 */
169         "pkt_header",           /* TEST7 */
170         "pkt_autokey",          /* TEST8 */
171         "pkt_crypto",           /* TEST9 */
172         "peer_stratum",         /* TEST10 */
173         "peer_dist",            /* TEST11 */
174         "peer_loop",            /* TEST12 */
175         "peer_unreach"          /* TEST13 */
176 };
177
178
179 int             ntpqmain        (int,   char **);
180 /*
181  * Built in command handler declarations
182  */
183 static  int     openhost        (const char *, int);
184 static  void    dump_hex_printable(const void *, size_t);
185 static  int     sendpkt         (void *, size_t);
186 static  int     getresponse     (int, int, u_short *, size_t *, const char **, int);
187 static  int     sendrequest     (int, associd_t, int, size_t, const char *);
188 static  char *  tstflags        (u_long);
189 #ifndef BUILD_AS_LIB
190 static  void    getcmds         (void);
191 #ifndef SYS_WINNT
192 static  int     abortcmd        (void);
193 #endif  /* SYS_WINNT */
194 static  void    docmd           (const char *);
195 static  void    tokenize        (const char *, char **, int *);
196 static  int     getarg          (const char *, int, arg_v *);
197 #endif  /* BUILD_AS_LIB */
198 static  int     findcmd         (const char *, struct xcmd *,
199                                  struct xcmd *, struct xcmd **);
200 static  int     rtdatetolfp     (char *, l_fp *);
201 static  int     decodearr       (char *, int *, l_fp *, int);
202 static  void    help            (struct parse *, FILE *);
203 static  int     helpsort        (const void *, const void *);
204 static  void    printusage      (struct xcmd *, FILE *);
205 static  void    timeout         (struct parse *, FILE *);
206 static  void    auth_delay      (struct parse *, FILE *);
207 static  void    host            (struct parse *, FILE *);
208 static  void    ntp_poll        (struct parse *, FILE *);
209 static  void    keyid           (struct parse *, FILE *);
210 static  void    keytype         (struct parse *, FILE *);
211 static  void    passwd          (struct parse *, FILE *);
212 static  void    hostnames       (struct parse *, FILE *);
213 static  void    setdebug        (struct parse *, FILE *);
214 static  void    quit            (struct parse *, FILE *);
215 static  void    showdrefid      (struct parse *, FILE *);
216 static  void    version         (struct parse *, FILE *);
217 static  void    raw             (struct parse *, FILE *);
218 static  void    cooked          (struct parse *, FILE *);
219 static  void    authenticate    (struct parse *, FILE *);
220 static  void    ntpversion      (struct parse *, FILE *);
221 static  void    warning         (const char *, ...)
222     __attribute__((__format__(__printf__, 1, 2)));
223 static  void    error           (const char *, ...)
224     __attribute__((__format__(__printf__, 1, 2)));
225 static  u_long  getkeyid        (const char *);
226 static  void    atoascii        (const char *, size_t, char *, size_t);
227 static  void    cookedprint     (int, size_t, const char *, int, int, FILE *);
228 static  void    rawprint        (int, size_t, const char *, int, int, FILE *);
229 static  void    startoutput     (void);
230 static  void    output          (FILE *, const char *, const char *);
231 static  void    endoutput       (FILE *);
232 static  void    outputarr       (FILE *, char *, int, l_fp *);
233 static  int     assoccmp        (const void *, const void *);
234 static  void    on_ctrlc        (void);
235         u_short varfmt          (const char *);
236 static  int     my_easprintf    (char**, const char *, ...) NTP_PRINTF(2, 3);
237 void    ntpq_custom_opt_handler (tOptions *, tOptDesc *);
238
239 /* read a character from memory and expand to integer */
240 static inline int
241 pgetc(
242         const char *cp
243         )
244 {
245         return (int)*(const unsigned char*)cp;
246 }
247
248
249 #ifdef OPENSSL
250 # ifdef HAVE_EVP_MD_DO_ALL_SORTED
251 static void list_md_fn(const EVP_MD *m, const char *from,
252                        const char *to, void *arg );
253 # endif
254 #endif
255 static char *insert_cmac(char *list);
256 static char *list_digest_names(void);
257
258 /*
259  * Built-in commands we understand
260  */
261 struct xcmd builtins[] = {
262         { "?",          help,           {  OPT|NTP_STR, NO, NO, NO },
263           { "command", "", "", "" },
264           "tell the use and syntax of commands" },
265         { "help",       help,           {  OPT|NTP_STR, NO, NO, NO },
266           { "command", "", "", "" },
267           "tell the use and syntax of commands" },
268         { "timeout",    timeout,        { OPT|NTP_UINT, NO, NO, NO },
269           { "msec", "", "", "" },
270           "set the primary receive time out" },
271         { "delay",      auth_delay,     { OPT|NTP_INT, NO, NO, NO },
272           { "msec", "", "", "" },
273           "set the delay added to encryption time stamps" },
274         { "host",       host,           { OPT|NTP_STR, OPT|NTP_STR, NO, NO },
275           { "-4|-6", "hostname", "", "" },
276           "specify the host whose NTP server we talk to" },
277         { "poll",       ntp_poll,       { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
278           { "n", "verbose", "", "" },
279           "poll an NTP server in client mode `n' times" },
280         { "passwd",     passwd,         { OPT|NTP_STR, NO, NO, NO },
281           { "", "", "", "" },
282           "specify a password to use for authenticated requests"},
283         { "hostnames",  hostnames,      { OPT|NTP_STR, NO, NO, NO },
284           { "yes|no", "", "", "" },
285           "specify whether hostnames or net numbers are printed"},
286         { "debug",      setdebug,       { OPT|NTP_STR, NO, NO, NO },
287           { "no|more|less", "", "", "" },
288           "set/change debugging level" },
289         { "quit",       quit,           { NO, NO, NO, NO },
290           { "", "", "", "" },
291           "exit ntpq" },
292         { "exit",       quit,           { NO, NO, NO, NO },
293           { "", "", "", "" },
294           "exit ntpq" },
295         { "keyid",      keyid,          { OPT|NTP_UINT, NO, NO, NO },
296           { "key#", "", "", "" },
297           "set keyid to use for authenticated requests" },
298         { "drefid",     showdrefid,     { OPT|NTP_STR, NO, NO, NO },
299           { "hash|ipv4", "", "", "" },
300           "display refid's as IPv4 or hash" },
301         { "version",    version,        { NO, NO, NO, NO },
302           { "", "", "", "" },
303           "print version number" },
304         { "raw",        raw,            { NO, NO, NO, NO },
305           { "", "", "", "" },
306           "do raw mode variable output" },
307         { "cooked",     cooked,         { NO, NO, NO, NO },
308           { "", "", "", "" },
309           "do cooked mode variable output" },
310         { "authenticate", authenticate, { OPT|NTP_STR, NO, NO, NO },
311           { "yes|no", "", "", "" },
312           "always authenticate requests to this server" },
313         { "ntpversion", ntpversion,     { OPT|NTP_UINT, NO, NO, NO },
314           { "version number", "", "", "" },
315           "set the NTP version number to use for requests" },
316         { "keytype",    keytype,        { OPT|NTP_STR, NO, NO, NO },
317           { "key type %s", "", "", "" },
318           NULL },
319         { 0,            0,              { NO, NO, NO, NO },
320           { "", "", "", "" }, "" }
321 };
322
323
324 /*
325  * Default values we use.
326  */
327 #define DEFHOST         "localhost"     /* default host name */
328 #define DEFTIMEOUT      5               /* wait 5 seconds for 1st pkt */
329 #define DEFSTIMEOUT     3               /* and 3 more for each additional */
330 /*
331  * Requests are automatically retried once, so total timeout with no
332  * response is a bit over 2 * DEFTIMEOUT, or 10 seconds.  At the other
333  * extreme, a request eliciting 32 packets of responses each for some
334  * reason nearly DEFSTIMEOUT seconds after the prior in that series,
335  * with a single packet dropped, would take around 32 * DEFSTIMEOUT, or
336  * 93 seconds to fail each of two times, or 186 seconds.
337  * Some commands involve a series of requests, such as "peers" and
338  * "mrulist", so the cumulative timeouts are even longer for those.
339  */
340 #define DEFDELAY        0x51EB852       /* 20 milliseconds, l_fp fraction */
341 #define LENHOSTNAME     256             /* host name is 256 characters long */
342 #define MAXCMDS         100             /* maximum commands on cmd line */
343 #define MAXHOSTS        200             /* maximum hosts on cmd line */
344 #define MAXLINE         512             /* maximum line length */
345 #define MAXTOKENS       (1+MAXARGS+2)   /* maximum number of usable tokens */
346 #define MAXVARLEN       256             /* maximum length of a variable name */
347 #define MAXVALLEN       2048            /* maximum length of a variable value */
348 #define MAXOUTLINE      72              /* maximum length of an output line */
349 #define SCREENWIDTH     76              /* nominal screen width in columns */
350
351 /*
352  * Some variables used and manipulated locally
353  */
354 struct sock_timeval tvout = { DEFTIMEOUT, 0 };  /* time out for reads */
355 struct sock_timeval tvsout = { DEFSTIMEOUT, 0 };/* secondary time out */
356 l_fp delay_time;                                /* delay time */
357 char currenthost[LENHOSTNAME];                  /* current host name */
358 int currenthostisnum;                           /* is prior text from IP? */
359 struct sockaddr_in hostaddr;                    /* host address */
360 int showhostnames = 1;                          /* show host names by default */
361 int wideremote = 0;                             /* show wide remote names? */
362
363 int ai_fam_templ;                               /* address family */
364 int ai_fam_default;                             /* default address family */
365 SOCKET sockfd;                                  /* fd socket is opened on */
366 int havehost = 0;                               /* set to 1 when host open */
367 int s_port = 0;
368 struct servent *server_entry = NULL;            /* server entry for ntp */
369
370
371 /*
372  * Sequence number used for requests.  It is incremented before
373  * it is used.
374  */
375 u_short sequence;
376
377 /*
378  * Holds data returned from queries.  Declare buffer long to be sure of
379  * alignment.
380  */
381 #define DATASIZE        (MAXFRAGS*480)  /* maximum amount of data */
382 long pktdata[DATASIZE/sizeof(long)];
383
384 /*
385  * assoc_cache[] is a dynamic array which allows references to
386  * associations using &1 ... &N for n associations, avoiding manual
387  * lookup of the current association IDs for a given ntpd.  It also
388  * caches the status word for each association, retrieved incidentally.
389  */
390 struct association *    assoc_cache;
391 u_int assoc_cache_slots;/* count of allocated array entries */
392 u_int numassoc;         /* number of cached associations */
393
394 /*
395  * For commands typed on the command line (with the -c option)
396  */
397 size_t numcmds = 0;
398 const char *ccmds[MAXCMDS];
399 #define ADDCMD(cp)      if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
400
401 /*
402  * When multiple hosts are specified.
403  */
404
405 u_int numhosts;
406
407 chost chosts[MAXHOSTS];
408 #define ADDHOST(cp)                                             \
409         do {                                                    \
410                 if (numhosts < MAXHOSTS) {                      \
411                         chosts[numhosts].name = (cp);           \
412                         chosts[numhosts].fam = ai_fam_templ;    \
413                         numhosts++;                             \
414                 }                                               \
415         } while (0)
416
417 /*
418  * Macro definitions we use
419  */
420 #define ISSPACE(c)      ((c) == ' ' || (c) == '\t')
421 #define ISEOL(c)        ((c) == '\n' || (c) == '\r' || (c) == '\0')
422 #define STREQ(a, b)     (*(a) == *(b) && strcmp((a), (b)) == 0)
423
424 /*
425  * Jump buffer for longjumping back to the command level
426  */
427 jmp_buf interrupt_buf;
428
429 /*
430  * Points at file being currently printed into
431  */
432 FILE *current_output;
433
434 /*
435  * Command table imported from ntpdc_ops.c
436  */
437 extern struct xcmd opcmds[];
438
439 char const *progname;
440
441 #ifdef NO_MAIN_ALLOWED
442 #ifndef BUILD_AS_LIB
443 CALL(ntpq,"ntpq",ntpqmain);
444
445 void clear_globals(void)
446 {
447         extern int ntp_optind;
448         showhostnames = 0;      /* don'tshow host names by default */
449         ntp_optind = 0;
450         server_entry = NULL;    /* server entry for ntp */
451         havehost = 0;           /* set to 1 when host open */
452         numassoc = 0;           /* number of cached associations */
453         numcmds = 0;
454         numhosts = 0;
455 }
456 #endif /* !BUILD_AS_LIB */
457 #endif /* NO_MAIN_ALLOWED */
458
459 /*
460  * main - parse arguments and handle options
461  */
462 #ifndef NO_MAIN_ALLOWED
463 int
464 main(
465         int argc,
466         char *argv[]
467         )
468 {
469         return ntpqmain(argc, argv);
470 }
471 #endif
472
473
474 #ifndef BUILD_AS_LIB
475 int
476 ntpqmain(
477         int argc,
478         char *argv[]
479         )
480 {
481         u_int ihost;
482         size_t icmd;
483
484
485 #ifdef SYS_VXWORKS
486         clear_globals();
487         taskPrioritySet(taskIdSelf(), 100 );
488 #endif
489
490         delay_time.l_ui = 0;
491         delay_time.l_uf = DEFDELAY;
492
493         init_lib();     /* sets up ipv4_works, ipv6_works */
494         ssl_applink();
495         init_auth();
496
497         /* Check to see if we have IPv6. Otherwise default to IPv4 */
498         if (!ipv6_works)
499                 ai_fam_default = AF_INET;
500
501         /* Fixup keytype's help based on available digest names */
502
503         {
504             char *list;
505             char *msg;
506
507             list = list_digest_names();
508
509             for (icmd = 0; icmd < sizeof(builtins)/sizeof(*builtins); icmd++) {
510                 if (strcmp("keytype", builtins[icmd].keyword) == 0) {
511                     break;
512                 }
513             }
514
515             /* CID: 1295478 */
516             /* This should only "trip" if "keytype" is removed from builtins */
517             INSIST(icmd < sizeof(builtins)/sizeof(*builtins));
518
519 #ifdef OPENSSL
520             builtins[icmd].desc[0] = "digest-name";
521             my_easprintf(&msg,
522                          "set key type to use for authenticated requests, one of:%s",
523                          list);
524 #else
525             builtins[icmd].desc[0] = "md5";
526             my_easprintf(&msg,
527                          "set key type to use for authenticated requests (%s)",
528                          list);
529 #endif
530             builtins[icmd].comment = msg;
531             free(list);
532         }
533
534         progname = argv[0];
535
536         {
537                 int optct = ntpOptionProcess(&ntpqOptions, argc, argv);
538                 argc -= optct;
539                 argv += optct;
540         }
541
542         /*
543          * Process options other than -c and -p, which are specially
544          * handled by ntpq_custom_opt_handler().
545          */
546
547         debug = OPT_VALUE_SET_DEBUG_LEVEL;
548
549         if (HAVE_OPT(IPV4))
550                 ai_fam_templ = AF_INET;
551         else if (HAVE_OPT(IPV6))
552                 ai_fam_templ = AF_INET6;
553         else
554                 ai_fam_templ = ai_fam_default;
555
556         if (HAVE_OPT(INTERACTIVE))
557                 interactive = 1;
558
559         if (HAVE_OPT(NUMERIC))
560                 showhostnames = 0;
561
562         if (HAVE_OPT(WIDE))
563                 wideremote = 1;
564
565         old_rv = HAVE_OPT(OLD_RV);
566
567         drefid = OPT_VALUE_REFID;
568
569         if (0 == argc) {
570                 ADDHOST(DEFHOST);
571         } else {
572                 for (ihost = 0; ihost < (u_int)argc; ihost++) {
573                         if ('-' == *argv[ihost]) {
574                                 //
575                                 // If I really cared I'd also check:
576                                 // 0 == argv[ihost][2]
577                                 //
578                                 // and there are other cases as well...
579                                 //
580                                 if ('4' == argv[ihost][1]) {
581                                         ai_fam_templ = AF_INET;
582                                         continue;
583                                 } else if ('6' == argv[ihost][1]) {
584                                         ai_fam_templ = AF_INET6;
585                                         continue;
586                                 } else {
587                                         // XXX Throw a usage error
588                                 }
589                         }
590                         ADDHOST(argv[ihost]);
591                 }
592         }
593
594         if (numcmds == 0 && interactive == 0
595             && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
596                 interactive = 1;
597         }
598
599         set_ctrl_c_hook(on_ctrlc);
600 #ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
601         if (interactive)
602                 push_ctrl_c_handler(abortcmd);
603 #endif /* SYS_WINNT */
604
605         if (numcmds == 0) {
606                 (void) openhost(chosts[0].name, chosts[0].fam);
607                 getcmds();
608         } else {
609                 for (ihost = 0; ihost < numhosts; ihost++) {
610                         if (openhost(chosts[ihost].name, chosts[ihost].fam)) {
611                                 if (ihost)
612                                         fputc('\n', current_output);
613                                 for (icmd = 0; icmd < numcmds; icmd++) {
614                                         if (icmd)
615                                                 fputc('\n', current_output);
616                                         docmd(ccmds[icmd]);
617                                 }
618                         }
619                 }
620         }
621 #ifdef SYS_WINNT
622         WSACleanup();
623 #endif /* SYS_WINNT */
624         return 0;
625 }
626 #endif /* !BUILD_AS_LIB */
627
628 /*
629  * openhost - open a socket to a host
630  */
631 static  int
632 openhost(
633         const char *hname,
634         int         fam
635         )
636 {
637         const char svc[] = "ntp";
638         char temphost[LENHOSTNAME];
639         int a_info, i;
640         struct addrinfo hints, *ai;
641         sockaddr_u addr;
642         size_t octets;
643         register const char *cp;
644         char name[LENHOSTNAME];
645
646         /*
647          * We need to get by the [] if they were entered
648          */
649
650         cp = hname;
651
652         if (*cp == '[') {
653                 cp++;
654                 for (i = 0; *cp && *cp != ']'; cp++, i++)
655                         name[i] = *cp;
656                 if (*cp == ']') {
657                         name[i] = '\0';
658                         hname = name;
659                 } else {
660                         return 0;
661                 }
662         }
663
664         /*
665          * First try to resolve it as an ip address and if that fails,
666          * do a fullblown (dns) lookup. That way we only use the dns
667          * when it is needed and work around some implementations that
668          * will return an "IPv4-mapped IPv6 address" address if you
669          * give it an IPv4 address to lookup.
670          */
671         ZERO(hints);
672         hints.ai_family = fam;
673         hints.ai_protocol = IPPROTO_UDP;
674         hints.ai_socktype = SOCK_DGRAM;
675         hints.ai_flags = Z_AI_NUMERICHOST;
676         ai = NULL;
677
678         a_info = getaddrinfo(hname, svc, &hints, &ai);
679         if (a_info == EAI_NONAME
680 #ifdef EAI_NODATA
681             || a_info == EAI_NODATA
682 #endif
683            ) {
684                 hints.ai_flags = AI_CANONNAME;
685 #ifdef AI_ADDRCONFIG
686                 hints.ai_flags |= AI_ADDRCONFIG;
687 #endif
688                 a_info = getaddrinfo(hname, svc, &hints, &ai);
689         }
690 #ifdef AI_ADDRCONFIG
691         /* Some older implementations don't like AI_ADDRCONFIG. */
692         if (a_info == EAI_BADFLAGS) {
693                 hints.ai_flags &= ~AI_ADDRCONFIG;
694                 a_info = getaddrinfo(hname, svc, &hints, &ai);
695         }
696 #endif
697         if (a_info != 0) {
698                 fprintf(stderr, "%s\n", gai_strerror(a_info));
699                 return 0;
700         }
701
702         INSIST(ai != NULL);
703         ZERO(addr);
704         octets = min(sizeof(addr), ai->ai_addrlen);
705         memcpy(&addr, ai->ai_addr, octets);
706
707         if (ai->ai_canonname == NULL) {
708                 strlcpy(temphost, stoa(&addr), sizeof(temphost));
709                 currenthostisnum = TRUE;
710         } else {
711                 strlcpy(temphost, ai->ai_canonname, sizeof(temphost));
712                 currenthostisnum = FALSE;
713         }
714
715         if (debug > 2)
716                 printf("Opening host %s (%s)\n",
717                         temphost,
718                         (ai->ai_family == AF_INET)
719                         ? "AF_INET"
720                         : (ai->ai_family == AF_INET6)
721                           ? "AF_INET6"
722                           : "AF-???"
723                         );
724
725         if (havehost == 1) {
726                 if (debug > 2)
727                         printf("Closing old host %s\n", currenthost);
728                 closesocket(sockfd);
729                 havehost = 0;
730         }
731         strlcpy(currenthost, temphost, sizeof(currenthost));
732
733         /* port maps to the same location in both families */
734         s_port = NSRCPORT(&addr);
735 #ifdef SYS_VXWORKS
736         ((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM);
737         if (ai->ai_family == AF_INET)
738                 *(struct sockaddr_in *)&hostaddr=
739                         *((struct sockaddr_in *)ai->ai_addr);
740         else
741                 *(struct sockaddr_in6 *)&hostaddr=
742                         *((struct sockaddr_in6 *)ai->ai_addr);
743 #endif /* SYS_VXWORKS */
744
745 #ifdef SYS_WINNT
746         {
747                 int optionValue = SO_SYNCHRONOUS_NONALERT;
748                 int err;
749
750                 err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
751                                  (void *)&optionValue, sizeof(optionValue));
752                 if (err) {
753                         mfprintf(stderr,
754                                  "setsockopt(SO_SYNCHRONOUS_NONALERT)"
755                                  " error: %m\n");
756                         freeaddrinfo(ai);
757                         exit(1);
758                 }
759         }
760 #endif /* SYS_WINNT */
761
762         sockfd = socket(ai->ai_family, ai->ai_socktype,
763                         ai->ai_protocol);
764         if (sockfd == INVALID_SOCKET) {
765                 error("socket");
766                 freeaddrinfo(ai);
767                 return 0;
768         }
769
770
771 #ifdef NEED_RCVBUF_SLOP
772 # ifdef SO_RCVBUF
773         { int rbufsize = DATASIZE + 2048;       /* 2K for slop */
774         if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
775                        (void *)&rbufsize, sizeof(int)) == -1)
776                 error("setsockopt");
777         }
778 # endif
779 #endif
780
781         if
782 #ifdef SYS_VXWORKS
783            (connect(sockfd, (struct sockaddr *)&hostaddr,
784                     sizeof(hostaddr)) == -1)
785 #else
786            (connect(sockfd, (struct sockaddr *)ai->ai_addr,
787                 ai->ai_addrlen) == -1)
788 #endif /* SYS_VXWORKS */
789         {
790                 error("connect");
791                 freeaddrinfo(ai);
792                 return 0;
793         }
794         freeaddrinfo(ai);
795         havehost = 1;
796         numassoc = 0;
797
798         return 1;
799 }
800
801
802 static void
803 dump_hex_printable(
804         const void *    data,
805         size_t          len
806         )
807 {
808         /* every line shows at most 16 bytes, so we need a buffer of
809          *   4 * 16 (2 xdigits, 1 char, one sep for xdigits)
810          * + 2 * 1  (block separators)
811          * + <LF> + <NUL>
812          *---------------
813          *  68 bytes
814          */
815         static const char s_xdig[16] = "0123456789ABCDEF";
816
817         char lbuf[68];
818         int  ch, rowlen;
819         const u_char * cdata = data;
820         char *xptr, *pptr;
821
822         while (len) {
823                 memset(lbuf, ' ', sizeof(lbuf));
824                 xptr = lbuf;
825                 pptr = lbuf + 3*16 + 2;
826
827                 rowlen = (len > 16) ? 16 : (int)len;
828                 len -= rowlen;
829                 
830                 do {
831                         ch = *cdata++;
832                         
833                         *xptr++ = s_xdig[ch >> 4  ];
834                         *xptr++ = s_xdig[ch & 0x0F];
835                         if (++xptr == lbuf + 3*8)
836                                 ++xptr;
837
838                         *pptr++ = isprint(ch) ? (char)ch : '.';
839                 } while (--rowlen);
840
841                 *pptr++ = '\n';
842                 *pptr   = '\0';
843                 fputs(lbuf, stdout);
844         }
845 }
846
847
848 /* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
849 /*
850  * sendpkt - send a packet to the remote host
851  */
852 static int
853 sendpkt(
854         void *  xdata,
855         size_t  xdatalen
856         )
857 {
858         if (debug >= 3)
859                 printf("Sending %zu octets\n", xdatalen);
860
861         if (send(sockfd, xdata, xdatalen, 0) == -1) {
862                 warning("write to %s failed", currenthost);
863                 return -1;
864         }
865
866         if (debug >= 4) {
867                 printf("Request packet:\n");
868                 dump_hex_printable(xdata, xdatalen);
869         }
870         return 0;
871 }
872
873 /*
874  * getresponse - get a (series of) response packet(s) and return the data
875  */
876 static int
877 getresponse(
878         int opcode,
879         int associd,
880         u_short *rstatus,
881         size_t *rsize,
882         const char **rdata,
883         int timeo
884         )
885 {
886         struct ntp_control rpkt;
887         struct sock_timeval tvo;
888         u_short offsets[MAXFRAGS+1];
889         u_short counts[MAXFRAGS+1];
890         u_short offset;
891         u_short count;
892         size_t numfrags;
893         size_t f;
894         size_t ff;
895         int seenlastfrag;
896         int shouldbesize;
897         fd_set fds;
898         int n;
899         int errcode;
900         /* absolute timeout checks. Not 'time_t' by intention! */
901         uint32_t tobase;        /* base value for timeout */
902         uint32_t tospan;        /* timeout span (max delay) */
903         uint32_t todiff;        /* current delay */
904
905         memset(offsets, 0, sizeof(offsets));
906         memset(counts , 0, sizeof(counts ));
907         
908         /*
909          * This is pretty tricky.  We may get between 1 and MAXFRAG packets
910          * back in response to the request.  We peel the data out of
911          * each packet and collect it in one long block.  When the last
912          * packet in the sequence is received we'll know how much data we
913          * should have had.  Note we use one long time out, should reconsider.
914          */
915         *rsize = 0;
916         if (rstatus)
917                 *rstatus = 0;
918         *rdata = (char *)pktdata;
919
920         numfrags = 0;
921         seenlastfrag = 0;
922
923         tobase = (uint32_t)time(NULL);
924         
925         FD_ZERO(&fds);
926
927         /*
928          * Loop until we have an error or a complete response.  Nearly all
929          * code paths to loop again use continue.
930          */
931         for (;;) {
932
933                 if (numfrags == 0)
934                         tvo = tvout;
935                 else
936                         tvo = tvsout;
937                 tospan = (uint32_t)tvo.tv_sec + (tvo.tv_usec != 0);
938
939                 FD_SET(sockfd, &fds);
940                 n = select(sockfd+1, &fds, NULL, NULL, &tvo);
941                 if (n == -1) {
942 #if !defined(SYS_WINNT) && defined(EINTR)
943                         /* Windows does not know about EINTR (until very
944                          * recently) and the handling of console events
945                          * is *very* different from POSIX/UNIX signal
946                          * handling anyway.
947                          *
948                          * Under non-windows targets we map EINTR as
949                          * 'last packet was received' and try to exit
950                          * the receive sequence.
951                          */
952                         if (errno == EINTR) {
953                                 seenlastfrag = 1;
954                                 goto maybe_final;
955                         }
956 #endif
957                         warning("select fails");
958                         return -1;
959                 }
960
961                 /*
962                  * Check if this is already too late. Trash the data and
963                  * fake a timeout if this is so.
964                  */
965                 todiff = (((uint32_t)time(NULL)) - tobase) & 0x7FFFFFFFu;
966                 if ((n > 0) && (todiff > tospan)) {
967                         n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
968                         n -= n; /* faked timeout return from 'select()',
969                                  * execute RMW cycle on 'n'
970                                  */
971                 }
972                 
973                 if (n <= 0) {
974                         /*
975                          * Timed out.  Return what we have
976                          */
977                         if (numfrags == 0) {
978                                 if (timeo)
979                                         fprintf(stderr,
980                                                 "%s: timed out, nothing received\n",
981                                                 currenthost);
982                                 return ERR_TIMEOUT;
983                         }
984                         if (timeo)
985                                 fprintf(stderr,
986                                         "%s: timed out with incomplete data\n",
987                                         currenthost);
988                         if (debug) {
989                                 fprintf(stderr,
990                                         "ERR_INCOMPLETE: Received fragments:\n");
991                                 for (f = 0; f < numfrags; f++)
992                                         fprintf(stderr,
993                                                 "%2u: %5d %5d\t%3d octets\n",
994                                                 (u_int)f, offsets[f],
995                                                 offsets[f] +
996                                                 counts[f],
997                                                 counts[f]);
998                                 fprintf(stderr,
999                                         "last fragment %sreceived\n",
1000                                         (seenlastfrag)
1001                                             ? ""
1002                                             : "not ");
1003                         }
1004                         return ERR_INCOMPLETE;
1005                 }
1006
1007                 n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
1008                 if (n < 0) {
1009                         warning("read");
1010                         return -1;
1011                 }
1012
1013                 if (debug >= 4) {
1014                         printf("Response packet:\n");
1015                         dump_hex_printable(&rpkt, n);
1016                 }
1017
1018                 /*
1019                  * Check for format errors.  Bug proofing.
1020                  */
1021                 if (n < (int)CTL_HEADER_LEN) {
1022                         if (debug)
1023                                 printf("Short (%d byte) packet received\n", n);
1024                         continue;
1025                 }
1026                 if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION
1027                     || PKT_VERSION(rpkt.li_vn_mode) < NTP_OLDVERSION) {
1028                         if (debug)
1029                                 printf("Packet received with version %d\n",
1030                                        PKT_VERSION(rpkt.li_vn_mode));
1031                         continue;
1032                 }
1033                 if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) {
1034                         if (debug)
1035                                 printf("Packet received with mode %d\n",
1036                                        PKT_MODE(rpkt.li_vn_mode));
1037                         continue;
1038                 }
1039                 if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) {
1040                         if (debug)
1041                                 printf("Received request packet, wanted response\n");
1042                         continue;
1043                 }
1044
1045                 /*
1046                  * Check opcode and sequence number for a match.
1047                  * Could be old data getting to us.
1048                  */
1049                 if (ntohs(rpkt.sequence) != sequence) {
1050                         if (debug)
1051                                 printf("Received sequnce number %d, wanted %d\n",
1052                                        ntohs(rpkt.sequence), sequence);
1053                         continue;
1054                 }
1055                 if (CTL_OP(rpkt.r_m_e_op) != opcode) {
1056                         if (debug)
1057                             printf(
1058                                     "Received opcode %d, wanted %d (sequence number okay)\n",
1059                                     CTL_OP(rpkt.r_m_e_op), opcode);
1060                         continue;
1061                 }
1062
1063                 /*
1064                  * Check the error code.  If non-zero, return it.
1065                  */
1066                 if (CTL_ISERROR(rpkt.r_m_e_op)) {
1067                         errcode = (ntohs(rpkt.status) >> 8) & 0xff;
1068                         if (CTL_ISMORE(rpkt.r_m_e_op))
1069                                 TRACE(1, ("Error code %d received on not-final packet\n",
1070                                           errcode));
1071                         if (errcode == CERR_UNSPEC)
1072                                 return ERR_UNSPEC;
1073                         return errcode;
1074                 }
1075
1076                 /*
1077                  * Check the association ID to make sure it matches what
1078                  * we sent.
1079                  */
1080                 if (ntohs(rpkt.associd) != associd) {
1081                         TRACE(1, ("Association ID %d doesn't match expected %d\n",
1082                                   ntohs(rpkt.associd), associd));
1083                         /*
1084                          * Hack for silly fuzzballs which, at the time of writing,
1085                          * return an assID of sys.peer when queried for system variables.
1086                          */
1087 #ifdef notdef
1088                         continue;
1089 #endif
1090                 }
1091
1092                 /*
1093                  * Collect offset and count.  Make sure they make sense.
1094                  */
1095                 offset = ntohs(rpkt.offset);
1096                 count = ntohs(rpkt.count);
1097
1098                 /*
1099                  * validate received payload size is padded to next 32-bit
1100                  * boundary and no smaller than claimed by rpkt.count
1101                  */
1102                 if (n & 0x3) {
1103                         TRACE(1, ("Response packet not padded, size = %d\n",
1104                                   n));
1105                         continue;
1106                 }
1107
1108                 shouldbesize = (CTL_HEADER_LEN + count + 3) & ~3;
1109
1110                 if (n < shouldbesize) {
1111                         printf("Response packet claims %u octets payload, above %ld received\n",
1112                                count, (long)(n - CTL_HEADER_LEN));
1113                         return ERR_INCOMPLETE;
1114                 }
1115
1116                 if (debug >= 3 && shouldbesize > n) {
1117                         u_int32 key;
1118                         u_int32 *lpkt;
1119                         int maclen;
1120
1121                         /*
1122                          * Usually we ignore authentication, but for debugging purposes
1123                          * we watch it here.
1124                          */
1125                         /* round to 8 octet boundary */
1126                         shouldbesize = (shouldbesize + 7) & ~7;
1127
1128                         maclen = n - shouldbesize;
1129                         if (maclen >= (int)MIN_MAC_LEN) {
1130                                 printf(
1131                                         "Packet shows signs of authentication (total %d, data %d, mac %d)\n",
1132                                         n, shouldbesize, maclen);
1133                                 lpkt = (u_int32 *)&rpkt;
1134                                 printf("%08lx %08lx %08lx %08lx %08lx %08lx\n",
1135                                        (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 3]),
1136                                        (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 2]),
1137                                        (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 1]),
1138                                        (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32)]),
1139                                        (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 1]),
1140                                        (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 2]));
1141                                 key = ntohl(lpkt[(n - maclen) / sizeof(u_int32)]);
1142                                 printf("Authenticated with keyid %lu\n", (u_long)key);
1143                                 if (key != 0 && key != info_auth_keyid) {
1144                                         printf("We don't know that key\n");
1145                                 } else {
1146                                         if (authdecrypt(key, (u_int32 *)&rpkt,
1147                                             n - maclen, maclen)) {
1148                                                 printf("Auth okay!\n");
1149                                         } else {
1150                                                 printf("Auth failed!\n");
1151                                         }
1152                                 }
1153                         }
1154                 }
1155
1156                 TRACE(2, ("Got packet, size = %d\n", n));
1157                 if (count > (n - CTL_HEADER_LEN)) {
1158                         TRACE(1, ("Received count of %u octets, data in packet is %ld\n",
1159                                   count, (long)n - CTL_HEADER_LEN));
1160                         continue;
1161                 }
1162                 if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) {
1163                         TRACE(1, ("Received count of 0 in non-final fragment\n"));
1164                         continue;
1165                 }
1166                 if (offset + count > sizeof(pktdata)) {
1167                         TRACE(1, ("Offset %u, count %u, too big for buffer\n",
1168                                   offset, count));
1169                         return ERR_TOOMUCH;
1170                 }
1171                 if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) {
1172                         TRACE(1, ("Received second last fragment packet\n"));
1173                         continue;
1174                 }
1175
1176                 /*
1177                  * So far, so good.  Record this fragment, making sure it doesn't
1178                  * overlap anything.
1179                  */
1180                 TRACE(2, ("Packet okay\n"));
1181
1182                 if (numfrags > (MAXFRAGS - 1)) {
1183                         TRACE(2, ("Number of fragments exceeds maximum %d\n",
1184                                   MAXFRAGS - 1));
1185                         return ERR_TOOMUCH;
1186                 }
1187
1188                 /*
1189                  * Find the position for the fragment relative to any
1190                  * previously received.
1191                  */
1192                 for (f = 0;
1193                      f < numfrags && offsets[f] < offset;
1194                      f++) {
1195                         /* empty body */ ;
1196                 }
1197
1198                 if (f < numfrags && offset == offsets[f]) {
1199                         TRACE(1, ("duplicate %u octets at %u ignored, prior %u at %u\n",
1200                                   count, offset, counts[f], offsets[f]));
1201                         continue;
1202                 }
1203
1204                 if (f > 0 && (offsets[f-1] + counts[f-1]) > offset) {
1205                         TRACE(1, ("received frag at %u overlaps with %u octet frag at %u\n",
1206                                   offset, counts[f-1], offsets[f-1]));
1207                         continue;
1208                 }
1209
1210                 if (f < numfrags && (offset + count) > offsets[f]) {
1211                         TRACE(1, ("received %u octet frag at %u overlaps with frag at %u\n",
1212                                   count, offset, offsets[f]));
1213                         continue;
1214                 }
1215
1216                 for (ff = numfrags; ff > f; ff--) {
1217                         offsets[ff] = offsets[ff-1];
1218                         counts[ff] = counts[ff-1];
1219                 }
1220                 offsets[f] = offset;
1221                 counts[f] = count;
1222                 numfrags++;
1223
1224                 /*
1225                  * Got that stuffed in right.  Figure out if this was the last.
1226                  * Record status info out of the last packet.
1227                  */
1228                 if (!CTL_ISMORE(rpkt.r_m_e_op)) {
1229                         seenlastfrag = 1;
1230                         if (rstatus != 0)
1231                                 *rstatus = ntohs(rpkt.status);
1232                 }
1233
1234                 /*
1235                  * Copy the data into the data buffer, and bump the
1236                  * timout base in case we need more.
1237                  */
1238                 memcpy((char *)pktdata + offset, &rpkt.u, count);
1239                 tobase = (uint32_t)time(NULL);
1240                 
1241                 /*
1242                  * If we've seen the last fragment, look for holes in the sequence.
1243                  * If there aren't any, we're done.
1244                  */
1245 #if !defined(SYS_WINNT) && defined(EINTR)
1246                 maybe_final:
1247 #endif
1248
1249                 if (seenlastfrag && offsets[0] == 0) {
1250                         for (f = 1; f < numfrags; f++)
1251                                 if (offsets[f-1] + counts[f-1] !=
1252                                     offsets[f])
1253                                         break;
1254                         if (f == numfrags) {
1255                                 *rsize = offsets[f-1] + counts[f-1];
1256                                 TRACE(1, ("%lu packets reassembled into response\n",
1257                                           (u_long)numfrags));
1258                                 return 0;
1259                         }
1260                 }
1261         }  /* giant for (;;) collecting response packets */
1262 }  /* getresponse() */
1263
1264
1265 /*
1266  * sendrequest - format and send a request packet
1267  */
1268 static int
1269 sendrequest(
1270         int opcode,
1271         associd_t associd,
1272         int auth,
1273         size_t qsize,
1274         const char *qdata
1275         )
1276 {
1277         struct ntp_control qpkt;
1278         size_t  pktsize;
1279         u_long  key_id;
1280         char *  pass;
1281         size_t  maclen;
1282
1283         /*
1284          * Check to make sure the data will fit in one packet
1285          */
1286         if (qsize > CTL_MAX_DATA_LEN) {
1287                 fprintf(stderr,
1288                         "***Internal error!  qsize (%zu) too large\n",
1289                         qsize);
1290                 return 1;
1291         }
1292
1293         /*
1294          * Fill in the packet
1295          */
1296         qpkt.li_vn_mode = PKT_LI_VN_MODE(0, pktversion, MODE_CONTROL);
1297         qpkt.r_m_e_op = (u_char)(opcode & CTL_OP_MASK);
1298         qpkt.sequence = htons(sequence);
1299         qpkt.status = 0;
1300         qpkt.associd = htons((u_short)associd);
1301         qpkt.offset = 0;
1302         qpkt.count = htons((u_short)qsize);
1303
1304         pktsize = CTL_HEADER_LEN;
1305
1306         /*
1307          * If we have data, copy and pad it out to a 32-bit boundary.
1308          */
1309         if (qsize > 0) {
1310                 memcpy(&qpkt.u, qdata, (size_t)qsize);
1311                 pktsize += qsize;
1312                 while (pktsize & (sizeof(u_int32) - 1)) {
1313                         qpkt.u.data[qsize++] = 0;
1314                         pktsize++;
1315                 }
1316         }
1317
1318         /*
1319          * If it isn't authenticated we can just send it.  Otherwise
1320          * we're going to have to think about it a little.
1321          */
1322         if (!auth && !always_auth) {
1323                 return sendpkt(&qpkt, pktsize);
1324         }
1325
1326         /*
1327          * Pad out packet to a multiple of 8 octets to be sure
1328          * receiver can handle it.
1329          */
1330         while (pktsize & 7) {
1331                 qpkt.u.data[qsize++] = 0;
1332                 pktsize++;
1333         }
1334
1335         /*
1336          * Get the keyid and the password if we don't have one.
1337          */
1338         if (info_auth_keyid == 0) {
1339                 key_id = getkeyid("Keyid: ");
1340                 if (key_id == 0 || key_id > NTP_MAXKEY) {
1341                         fprintf(stderr,
1342                                 "Invalid key identifier\n");
1343                         return 1;
1344                 }
1345                 info_auth_keyid = key_id;
1346         }
1347         if (!authistrusted(info_auth_keyid)) {
1348                 pass = getpass_keytype(info_auth_keytype);
1349                 if ('\0' == pass[0]) {
1350                         fprintf(stderr, "Invalid password\n");
1351                         return 1;
1352                 }
1353                 authusekey(info_auth_keyid, info_auth_keytype,
1354                            (u_char *)pass);
1355                 authtrust(info_auth_keyid, 1);
1356         }
1357
1358         /*
1359          * Do the encryption.
1360          */
1361         maclen = authencrypt(info_auth_keyid, (void *)&qpkt, pktsize);
1362         if (!maclen) {
1363                 fprintf(stderr, "Key not found\n");
1364                 return 1;
1365         } else if ((size_t)maclen != (info_auth_hashlen + sizeof(keyid_t))) {
1366                 fprintf(stderr,
1367                         "%zu octet MAC, %zu expected with %zu octet digest\n",
1368                         maclen, (info_auth_hashlen + sizeof(keyid_t)),
1369                         info_auth_hashlen);
1370                 return 1;
1371         }
1372
1373         return sendpkt((char *)&qpkt, pktsize + maclen);
1374 }
1375
1376
1377 /*
1378  * show_error_msg - display the error text for a mode 6 error response.
1379  */
1380 void
1381 show_error_msg(
1382         int             m6resp,
1383         associd_t       associd
1384         )
1385 {
1386         if (numhosts > 1)
1387                 fprintf(stderr, "server=%s ", currenthost);
1388
1389         switch (m6resp) {
1390
1391         case CERR_BADFMT:
1392                 fprintf(stderr,
1393                     "***Server reports a bad format request packet\n");
1394                 break;
1395
1396         case CERR_PERMISSION:
1397                 fprintf(stderr,
1398                     "***Server disallowed request (authentication?)\n");
1399                 break;
1400
1401         case CERR_BADOP:
1402                 fprintf(stderr,
1403                     "***Server reports a bad opcode in request\n");
1404                 break;
1405
1406         case CERR_BADASSOC:
1407                 fprintf(stderr,
1408                     "***Association ID %d unknown to server\n",
1409                     associd);
1410                 break;
1411
1412         case CERR_UNKNOWNVAR:
1413                 fprintf(stderr,
1414                     "***A request variable unknown to the server\n");
1415                 break;
1416
1417         case CERR_BADVALUE:
1418                 fprintf(stderr,
1419                     "***Server indicates a request variable was bad\n");
1420                 break;
1421
1422         case ERR_UNSPEC:
1423                 fprintf(stderr,
1424                     "***Server returned an unspecified error\n");
1425                 break;
1426
1427         case ERR_TIMEOUT:
1428                 fprintf(stderr, "***Request timed out\n");
1429                 break;
1430
1431         case ERR_INCOMPLETE:
1432                 fprintf(stderr,
1433                     "***Response from server was incomplete\n");
1434                 break;
1435
1436         case ERR_TOOMUCH:
1437                 fprintf(stderr,
1438                     "***Buffer size exceeded for returned data\n");
1439                 break;
1440
1441         default:
1442                 fprintf(stderr,
1443                     "***Server returns unknown error code %d\n",
1444                     m6resp);
1445         }
1446 }
1447
1448 /*
1449  * doquery - send a request and process the response, displaying
1450  *           error messages for any error responses.
1451  */
1452 int
1453 doquery(
1454         int opcode,
1455         associd_t associd,
1456         int auth,
1457         size_t qsize,
1458         const char *qdata,
1459         u_short *rstatus,
1460         size_t *rsize,
1461         const char **rdata
1462         )
1463 {
1464         return doqueryex(opcode, associd, auth, qsize, qdata, rstatus,
1465                          rsize, rdata, FALSE);
1466 }
1467
1468
1469 /*
1470  * doqueryex - send a request and process the response, optionally
1471  *             displaying error messages for any error responses.
1472  */
1473 int
1474 doqueryex(
1475         int opcode,
1476         associd_t associd,
1477         int auth,
1478         size_t qsize,
1479         const char *qdata,
1480         u_short *rstatus,
1481         size_t *rsize,
1482         const char **rdata,
1483         int quiet
1484         )
1485 {
1486         int res;
1487         int done;
1488
1489         /*
1490          * Check to make sure host is open
1491          */
1492         if (!havehost) {
1493                 fprintf(stderr, "***No host open, use `host' command\n");
1494                 return -1;
1495         }
1496
1497         done = 0;
1498         sequence++;
1499
1500     again:
1501         /*
1502          * send a request
1503          */
1504         res = sendrequest(opcode, associd, auth, qsize, qdata);
1505         if (res != 0)
1506                 return res;
1507
1508         /*
1509          * Get the response.  If we got a standard error, print a message
1510          */
1511         res = getresponse(opcode, associd, rstatus, rsize, rdata, done);
1512
1513         if (res > 0) {
1514                 if (!done && (res == ERR_TIMEOUT || res == ERR_INCOMPLETE)) {
1515                         if (res == ERR_INCOMPLETE) {
1516                                 /*
1517                                  * better bump the sequence so we don't
1518                                  * get confused about differing fragments.
1519                                  */
1520                                 sequence++;
1521                         }
1522                         done = 1;
1523                         goto again;
1524                 }
1525                 if (!quiet)
1526                         show_error_msg(res, associd);
1527
1528         }
1529         return res;
1530 }
1531
1532
1533 #ifndef BUILD_AS_LIB
1534 /*
1535  * getcmds - read commands from the standard input and execute them
1536  */
1537 static void
1538 getcmds(void)
1539 {
1540         char *  line;
1541         int     count;
1542
1543         ntp_readline_init(interactive ? prompt : NULL);
1544
1545         for (;;) {
1546                 line = ntp_readline(&count);
1547                 if (NULL == line)
1548                         break;
1549                 docmd(line);
1550                 free(line);
1551         }
1552
1553         ntp_readline_uninit();
1554 }
1555 #endif /* !BUILD_AS_LIB */
1556
1557
1558 #if !defined(SYS_WINNT) && !defined(BUILD_AS_LIB)
1559 /*
1560  * abortcmd - catch interrupts and abort the current command
1561  */
1562 static int
1563 abortcmd(void)
1564 {
1565         if (current_output == stdout)
1566                 (void) fflush(stdout);
1567         putc('\n', stderr);
1568         (void) fflush(stderr);
1569         if (jump) {
1570                 jump = 0;
1571                 longjmp(interrupt_buf, 1);
1572         }
1573         return TRUE;
1574 }
1575 #endif  /* !SYS_WINNT && !BUILD_AS_LIB */
1576
1577
1578 #ifndef BUILD_AS_LIB
1579 /*
1580  * docmd - decode the command line and execute a command
1581  */
1582 static void
1583 docmd(
1584         const char *cmdline
1585         )
1586 {
1587         char *tokens[1+MAXARGS+2];
1588         struct parse pcmd;
1589         int ntok;
1590         static int i;
1591         struct xcmd *xcmd;
1592
1593         /*
1594          * Tokenize the command line.  If nothing on it, return.
1595          */
1596         tokenize(cmdline, tokens, &ntok);
1597         if (ntok == 0)
1598             return;
1599
1600         /*
1601          * Find the appropriate command description.
1602          */
1603         i = findcmd(tokens[0], builtins, opcmds, &xcmd);
1604         if (i == 0) {
1605                 (void) fprintf(stderr, "***Command `%s' unknown\n",
1606                                tokens[0]);
1607                 return;
1608         } else if (i >= 2) {
1609                 (void) fprintf(stderr, "***Command `%s' ambiguous\n",
1610                                tokens[0]);
1611                 return;
1612         }
1613
1614         /* Warn about ignored extra args */
1615         for (i = MAXARGS + 1; i < ntok ; ++i) {
1616                 fprintf(stderr, "***Extra arg `%s' ignored\n", tokens[i]);
1617         }
1618
1619         /*
1620          * Save the keyword, then walk through the arguments, interpreting
1621          * as we go.
1622          */
1623         pcmd.keyword = tokens[0];
1624         pcmd.nargs = 0;
1625         for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) {
1626                 if ((i+1) >= ntok) {
1627                         if (!(xcmd->arg[i] & OPT)) {
1628                                 printusage(xcmd, stderr);
1629                                 return;
1630                         }
1631                         break;
1632                 }
1633                 if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>'))
1634                         break;
1635                 if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i]))
1636                         return;
1637                 pcmd.nargs++;
1638         }
1639
1640         i++;
1641         if (i < ntok && *tokens[i] == '>') {
1642                 char *fname;
1643
1644                 if (*(tokens[i]+1) != '\0')
1645                         fname = tokens[i]+1;
1646                 else if ((i+1) < ntok)
1647                         fname = tokens[i+1];
1648                 else {
1649                         (void) fprintf(stderr, "***No file for redirect\n");
1650                         return;
1651                 }
1652
1653                 current_output = fopen(fname, "w");
1654                 if (current_output == NULL) {
1655                         (void) fprintf(stderr, "***Error opening %s: ", fname);
1656                         perror("");
1657                         return;
1658                 }
1659                 i = 1;          /* flag we need a close */
1660         } else {
1661                 current_output = stdout;
1662                 i = 0;          /* flag no close */
1663         }
1664
1665         if (interactive && setjmp(interrupt_buf)) {
1666                 jump = 0;
1667                 return;
1668         } else {
1669                 jump++;
1670                 (xcmd->handler)(&pcmd, current_output);
1671                 jump = 0;       /* HMS: 961106: was after fclose() */
1672                 if (i) (void) fclose(current_output);
1673         }
1674
1675         return;
1676 }
1677
1678
1679 /*
1680  * tokenize - turn a command line into tokens
1681  *
1682  * SK: Modified to allow a quoted string
1683  *
1684  * HMS: If the first character of the first token is a ':' then (after
1685  * eating inter-token whitespace) the 2nd token is the rest of the line.
1686  */
1687
1688 static void
1689 tokenize(
1690         const char *line,
1691         char **tokens,
1692         int *ntok
1693         )
1694 {
1695         register const char *cp;
1696         register char *sp;
1697         static char tspace[MAXLINE];
1698
1699         sp = tspace;
1700         cp = line;
1701         for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
1702                 tokens[*ntok] = sp;
1703
1704                 /* Skip inter-token whitespace */
1705                 while (ISSPACE(*cp))
1706                     cp++;
1707
1708                 /* If we're at EOL we're done */
1709                 if (ISEOL(*cp))
1710                     break;
1711
1712                 /* If this is the 2nd token and the first token begins
1713                  * with a ':', then just grab to EOL.
1714                  */
1715
1716                 if (*ntok == 1 && tokens[0][0] == ':') {
1717                         do {
1718                                 if (sp - tspace >= MAXLINE)
1719                                         goto toobig;
1720                                 *sp++ = *cp++;
1721                         } while (!ISEOL(*cp));
1722                 }
1723
1724                 /* Check if this token begins with a double quote.
1725                  * If yes, continue reading till the next double quote
1726                  */
1727                 else if (*cp == '\"') {
1728                         ++cp;
1729                         do {
1730                                 if (sp - tspace >= MAXLINE)
1731                                         goto toobig;
1732                                 *sp++ = *cp++;
1733                         } while ((*cp != '\"') && !ISEOL(*cp));
1734                         /* HMS: a missing closing " should be an error */
1735                 }
1736                 else {
1737                         do {
1738                                 if (sp - tspace >= MAXLINE)
1739                                         goto toobig;
1740                                 *sp++ = *cp++;
1741                         } while ((*cp != '\"') && !ISSPACE(*cp) && !ISEOL(*cp));
1742                         /* HMS: Why check for a " in the previous line? */
1743                 }
1744
1745                 if (sp - tspace >= MAXLINE)
1746                         goto toobig;
1747                 *sp++ = '\0';
1748         }
1749         return;
1750
1751   toobig:
1752         *ntok = 0;
1753         fprintf(stderr,
1754                 "***Line `%s' is too big\n",
1755                 line);
1756         return;
1757 }
1758
1759
1760 /*
1761  * getarg - interpret an argument token
1762  */
1763 static int
1764 getarg(
1765         const char *str,
1766         int code,
1767         arg_v *argp
1768         )
1769 {
1770         u_long ul;
1771
1772         switch (code & ~OPT) {
1773         case NTP_STR:
1774                 argp->string = str;
1775                 break;
1776
1777         case NTP_ADD:
1778                 if (!getnetnum(str, &argp->netnum, NULL, 0))
1779                         return 0;
1780                 break;
1781
1782         case NTP_UINT:
1783                 if ('&' == str[0]) {
1784                         if (!atouint(&str[1], &ul)) {
1785                                 fprintf(stderr,
1786                                         "***Association index `%s' invalid/undecodable\n",
1787                                         str);
1788                                 return 0;
1789                         }
1790                         if (0 == numassoc) {
1791                                 dogetassoc(stdout);
1792                                 if (0 == numassoc) {
1793                                         fprintf(stderr,
1794                                                 "***No associations found, `%s' unknown\n",
1795                                                 str);
1796                                         return 0;
1797                                 }
1798                         }
1799                         ul = min(ul, numassoc);
1800                         argp->uval = assoc_cache[ul - 1].assid;
1801                         break;
1802                 }
1803                 if (!atouint(str, &argp->uval)) {
1804                         fprintf(stderr, "***Illegal unsigned value %s\n",
1805                                 str);
1806                         return 0;
1807                 }
1808                 break;
1809
1810         case NTP_INT:
1811                 if (!atoint(str, &argp->ival)) {
1812                         fprintf(stderr, "***Illegal integer value %s\n",
1813                                 str);
1814                         return 0;
1815                 }
1816                 break;
1817
1818         case IP_VERSION:
1819                 if (!strcmp("-6", str)) {
1820                         argp->ival = 6;
1821                 } else if (!strcmp("-4", str)) {
1822                         argp->ival = 4;
1823                 } else {
1824                         fprintf(stderr, "***Version must be either 4 or 6\n");
1825                         return 0;
1826                 }
1827                 break;
1828         }
1829
1830         return 1;
1831 }
1832 #endif  /* !BUILD_AS_LIB */
1833
1834
1835 /*
1836  * findcmd - find a command in a command description table
1837  */
1838 static int
1839 findcmd(
1840         const char *    str,
1841         struct xcmd *   clist1,
1842         struct xcmd *   clist2,
1843         struct xcmd **  cmd
1844         )
1845 {
1846         struct xcmd *cl;
1847         size_t clen;
1848         int nmatch;
1849         struct xcmd *nearmatch = NULL;
1850         struct xcmd *clist;
1851
1852         clen = strlen(str);
1853         nmatch = 0;
1854         if (clist1 != 0)
1855             clist = clist1;
1856         else if (clist2 != 0)
1857             clist = clist2;
1858         else
1859             return 0;
1860
1861     again:
1862         for (cl = clist; cl->keyword != 0; cl++) {
1863                 /* do a first character check, for efficiency */
1864                 if (*str != *(cl->keyword))
1865                     continue;
1866                 if (strncmp(str, cl->keyword, (unsigned)clen) == 0) {
1867                         /*
1868                          * Could be extact match, could be approximate.
1869                          * Is exact if the length of the keyword is the
1870                          * same as the str.
1871                          */
1872                         if (*((cl->keyword) + clen) == '\0') {
1873                                 *cmd = cl;
1874                                 return 1;
1875                         }
1876                         nmatch++;
1877                         nearmatch = cl;
1878                 }
1879         }
1880
1881         /*
1882          * See if there is more to do.  If so, go again.  Sorry about the
1883          * goto, too much looking at BSD sources...
1884          */
1885         if (clist == clist1 && clist2 != 0) {
1886                 clist = clist2;
1887                 goto again;
1888         }
1889
1890         /*
1891          * If we got extactly 1 near match, use it, else return number
1892          * of matches.
1893          */
1894         if (nmatch == 1) {
1895                 *cmd = nearmatch;
1896                 return 1;
1897         }
1898         return nmatch;
1899 }
1900
1901
1902 /*
1903  * getnetnum - given a host name, return its net number
1904  *             and (optional) full name
1905  */
1906 int
1907 getnetnum(
1908         const char *hname,
1909         sockaddr_u *num,
1910         char *fullhost,
1911         int af
1912         )
1913 {
1914         struct addrinfo hints, *ai = NULL;
1915
1916         ZERO(hints);
1917         hints.ai_flags = AI_CANONNAME;
1918 #ifdef AI_ADDRCONFIG
1919         hints.ai_flags |= AI_ADDRCONFIG;
1920 #endif
1921
1922         /*
1923          * decodenetnum only works with addresses, but handles syntax
1924          * that getaddrinfo doesn't:  [2001::1]:1234
1925          */
1926         if (decodenetnum(hname, num)) {
1927                 if (fullhost != NULL)
1928                         getnameinfo(&num->sa, SOCKLEN(num), fullhost,
1929                                     LENHOSTNAME, NULL, 0, 0);
1930                 return 1;
1931         } else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) {
1932                 INSIST(sizeof(*num) >= ai->ai_addrlen);
1933                 memcpy(num, ai->ai_addr, ai->ai_addrlen);
1934                 if (fullhost != NULL) {
1935                         if (ai->ai_canonname != NULL)
1936                                 strlcpy(fullhost, ai->ai_canonname,
1937                                         LENHOSTNAME);
1938                         else
1939                                 getnameinfo(&num->sa, SOCKLEN(num),
1940                                             fullhost, LENHOSTNAME, NULL,
1941                                             0, 0);
1942                 }
1943                 freeaddrinfo(ai);
1944                 return 1;
1945         }
1946         fprintf(stderr, "***Can't find host %s\n", hname);
1947
1948         return 0;
1949 }
1950
1951
1952 /*
1953  * nntohost - convert network number to host name.  This routine enforces
1954  *             the showhostnames setting.
1955  */
1956 const char *
1957 nntohost(
1958         sockaddr_u *netnum
1959         )
1960 {
1961         return nntohost_col(netnum, LIB_BUFLENGTH - 1, FALSE);
1962 }
1963
1964
1965 /*
1966  * nntohost_col - convert network number to host name in fixed width.
1967  *                This routine enforces the showhostnames setting.
1968  *                When displaying hostnames longer than the width,
1969  *                the first part of the hostname is displayed.  When
1970  *                displaying numeric addresses longer than the width,
1971  *                Such as IPv6 addresses, the caller decides whether
1972  *                the first or last of the numeric address is used.
1973  */
1974 const char *
1975 nntohost_col(
1976         sockaddr_u *    addr,
1977         size_t          width,
1978         int             preserve_lowaddrbits
1979         )
1980 {
1981         const char *    out;
1982
1983         if (!showhostnames || SOCK_UNSPEC(addr)) {
1984                 if (preserve_lowaddrbits)
1985                         out = trunc_left(stoa(addr), width);
1986                 else
1987                         out = trunc_right(stoa(addr), width);
1988         } else if (ISREFCLOCKADR(addr)) {
1989                 out = refnumtoa(addr);
1990         } else {
1991                 out = trunc_right(socktohost(addr), width);
1992         }
1993         return out;
1994 }
1995
1996
1997 /*
1998  * nntohostp() is the same as nntohost() plus a :port suffix
1999  */
2000 const char *
2001 nntohostp(
2002         sockaddr_u *netnum
2003         )
2004 {
2005         const char *    hostn;
2006         char *          buf;
2007
2008         if (!showhostnames || SOCK_UNSPEC(netnum))
2009                 return sptoa(netnum);
2010         else if (ISREFCLOCKADR(netnum))
2011                 return refnumtoa(netnum);
2012
2013         hostn = socktohost(netnum);
2014         LIB_GETBUF(buf);
2015         snprintf(buf, LIB_BUFLENGTH, "%s:%u", hostn, SRCPORT(netnum));
2016
2017         return buf;
2018 }
2019
2020 /*
2021  * rtdatetolfp - decode an RT-11 date into an l_fp
2022  */
2023 static int
2024 rtdatetolfp(
2025         char *str,
2026         l_fp *lfp
2027         )
2028 {
2029         register char *cp;
2030         register int i;
2031         struct calendar cal;
2032         char buf[4];
2033
2034         cal.yearday = 0;
2035
2036         /*
2037          * An RT-11 date looks like:
2038          *
2039          * d[d]-Mth-y[y] hh:mm:ss
2040          *
2041          * (No docs, but assume 4-digit years are also legal...)
2042          *
2043          * d[d]-Mth-y[y[y[y]]] hh:mm:ss
2044          */
2045         cp = str;
2046         if (!isdigit(pgetc(cp))) {
2047                 if (*cp == '-') {
2048                         /*
2049                          * Catch special case
2050                          */
2051                         L_CLR(lfp);
2052                         return 1;
2053                 }
2054                 return 0;
2055         }
2056
2057         cal.monthday = (u_char) (*cp++ - '0');  /* ascii dependent */
2058         if (isdigit(pgetc(cp))) {
2059                 cal.monthday = (u_char)((cal.monthday << 3) + (cal.monthday << 1));
2060                 cal.monthday = (u_char)(cal.monthday + *cp++ - '0');
2061         }
2062
2063         if (*cp++ != '-')
2064             return 0;
2065
2066         for (i = 0; i < 3; i++)
2067             buf[i] = *cp++;
2068         buf[3] = '\0';
2069
2070         for (i = 0; i < 12; i++)
2071             if (STREQ(buf, months[i]))
2072                 break;
2073         if (i == 12)
2074             return 0;
2075         cal.month = (u_char)(i + 1);
2076
2077         if (*cp++ != '-')
2078             return 0;
2079
2080         if (!isdigit(pgetc(cp)))
2081             return 0;
2082         cal.year = (u_short)(*cp++ - '0');
2083         if (isdigit(pgetc(cp))) {
2084                 cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2085                 cal.year = (u_short)(*cp++ - '0');
2086         }
2087         if (isdigit(pgetc(cp))) {
2088                 cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2089                 cal.year = (u_short)(cal.year + *cp++ - '0');
2090         }
2091         if (isdigit(pgetc(cp))) {
2092                 cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2093                 cal.year = (u_short)(cal.year + *cp++ - '0');
2094         }
2095
2096         /*
2097          * Catch special case.  If cal.year == 0 this is a zero timestamp.
2098          */
2099         if (cal.year == 0) {
2100                 L_CLR(lfp);
2101                 return 1;
2102         }
2103
2104         if (*cp++ != ' ' || !isdigit(pgetc(cp)))
2105             return 0;
2106         cal.hour = (u_char)(*cp++ - '0');
2107         if (isdigit(pgetc(cp))) {
2108                 cal.hour = (u_char)((cal.hour << 3) + (cal.hour << 1));
2109                 cal.hour = (u_char)(cal.hour + *cp++ - '0');
2110         }
2111
2112         if (*cp++ != ':' || !isdigit(pgetc(cp)))
2113             return 0;
2114         cal.minute = (u_char)(*cp++ - '0');
2115         if (isdigit(pgetc(cp))) {
2116                 cal.minute = (u_char)((cal.minute << 3) + (cal.minute << 1));
2117                 cal.minute = (u_char)(cal.minute + *cp++ - '0');
2118         }
2119
2120         if (*cp++ != ':' || !isdigit(pgetc(cp)))
2121             return 0;
2122         cal.second = (u_char)(*cp++ - '0');
2123         if (isdigit(pgetc(cp))) {
2124                 cal.second = (u_char)((cal.second << 3) + (cal.second << 1));
2125                 cal.second = (u_char)(cal.second + *cp++ - '0');
2126         }
2127
2128         /*
2129          * For RT-11, 1972 seems to be the pivot year
2130          */
2131         if (cal.year < 72)
2132                 cal.year += 2000;
2133         if (cal.year < 100)
2134                 cal.year += 1900;
2135
2136         lfp->l_ui = caltontp(&cal);
2137         lfp->l_uf = 0;
2138         return 1;
2139 }
2140
2141
2142 /*
2143  * decodets - decode a timestamp into an l_fp format number, with
2144  *            consideration of fuzzball formats.
2145  */
2146 int
2147 decodets(
2148         char *str,
2149         l_fp *lfp
2150         )
2151 {
2152         char *cp;
2153         char buf[30];
2154         size_t b;
2155
2156         /*
2157          * If it starts with a 0x, decode as hex.
2158          */
2159         if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
2160                 return hextolfp(str+2, lfp);
2161
2162         /*
2163          * If it starts with a '"', try it as an RT-11 date.
2164          */
2165         if (*str == '"') {
2166                 cp = str + 1;
2167                 b = 0;
2168                 while ('"' != *cp && '\0' != *cp &&
2169                        b < COUNTOF(buf) - 1)
2170                         buf[b++] = *cp++;
2171                 buf[b] = '\0';
2172                 return rtdatetolfp(buf, lfp);
2173         }
2174
2175         /*
2176          * Might still be hex.  Check out the first character.  Talk
2177          * about heuristics!
2178          */
2179         if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f'))
2180                 return hextolfp(str, lfp);
2181
2182         /*
2183          * Try it as a decimal.  If this fails, try as an unquoted
2184          * RT-11 date.  This code should go away eventually.
2185          */
2186         if (atolfp(str, lfp))
2187                 return 1;
2188
2189         return rtdatetolfp(str, lfp);
2190 }
2191
2192
2193 /*
2194  * decodetime - decode a time value.  It should be in milliseconds
2195  */
2196 int
2197 decodetime(
2198         char *str,
2199         l_fp *lfp
2200         )
2201 {
2202         return mstolfp(str, lfp);
2203 }
2204
2205
2206 /*
2207  * decodeint - decode an integer
2208  */
2209 int
2210 decodeint(
2211         char *str,
2212         long *val
2213         )
2214 {
2215         if (*str == '0') {
2216                 if (*(str+1) == 'x' || *(str+1) == 'X')
2217                     return hextoint(str+2, (u_long *)val);
2218                 return octtoint(str, (u_long *)val);
2219         }
2220         return atoint(str, val);
2221 }
2222
2223
2224 /*
2225  * decodeuint - decode an unsigned integer
2226  */
2227 int
2228 decodeuint(
2229         char *str,
2230         u_long *val
2231         )
2232 {
2233         if (*str == '0') {
2234                 if (*(str + 1) == 'x' || *(str + 1) == 'X')
2235                         return (hextoint(str + 2, val));
2236                 return (octtoint(str, val));
2237         }
2238         return (atouint(str, val));
2239 }
2240
2241
2242 /*
2243  * decodearr - decode an array of time values
2244  */
2245 static int
2246 decodearr(
2247         char *cp,
2248         int  *narr,
2249         l_fp *lfpa,
2250         int   amax
2251         )
2252 {
2253         char *bp;
2254         char buf[60];
2255
2256         *narr = 0;
2257
2258         while (*narr < amax && *cp) {
2259                 if (isspace(pgetc(cp))) {
2260                         do
2261                                 ++cp;
2262                         while (*cp && isspace(pgetc(cp)));
2263                 } else {
2264                         bp = buf;
2265                         do {
2266                                 if (bp != (buf + sizeof(buf) - 1))
2267                                         *bp++ = *cp;
2268                                 ++cp;
2269                         } while (*cp && !isspace(pgetc(cp)));
2270                         *bp = '\0';
2271
2272                         if (!decodetime(buf, lfpa))
2273                                 return 0;
2274                         ++(*narr);
2275                         ++lfpa;
2276                 }
2277         }
2278         return 1;
2279 }
2280
2281
2282 /*
2283  * Finally, the built in command handlers
2284  */
2285
2286 /*
2287  * help - tell about commands, or details of a particular command
2288  */
2289 static void
2290 help(
2291         struct parse *pcmd,
2292         FILE *fp
2293         )
2294 {
2295         struct xcmd *xcp = NULL;        /* quiet warning */
2296         const char *cmd;
2297         const char *list[100];
2298         size_t word, words;
2299         size_t row, rows;
2300         size_t col, cols;
2301         size_t length;
2302
2303         if (pcmd->nargs == 0) {
2304                 words = 0;
2305                 for (xcp = builtins; xcp->keyword != NULL; xcp++) {
2306                         if (*(xcp->keyword) != '?' &&
2307                             words < COUNTOF(list))
2308                                 list[words++] = xcp->keyword;
2309                 }
2310                 for (xcp = opcmds; xcp->keyword != NULL; xcp++)
2311                         if (words < COUNTOF(list))
2312                                 list[words++] = xcp->keyword;
2313
2314                 qsort((void *)list, words, sizeof(list[0]), helpsort);
2315                 col = 0;
2316                 for (word = 0; word < words; word++) {
2317                         length = strlen(list[word]);
2318                         col = max(col, length);
2319                 }
2320
2321                 cols = SCREENWIDTH / ++col;
2322                 rows = (words + cols - 1) / cols;
2323
2324                 fprintf(fp, "ntpq commands:\n");
2325
2326                 for (row = 0; row < rows; row++) {
2327                         for (word = row; word < words; word += rows)
2328                                 fprintf(fp, "%-*.*s", (int)col,
2329                                         (int)col - 1, list[word]);
2330                         fprintf(fp, "\n");
2331                 }
2332         } else {
2333                 cmd = pcmd->argval[0].string;
2334                 words = findcmd(cmd, builtins, opcmds, &xcp);
2335                 if (words == 0) {
2336                         fprintf(stderr,
2337                                 "Command `%s' is unknown\n", cmd);
2338                         return;
2339                 } else if (words >= 2) {
2340                         fprintf(stderr,
2341                                 "Command `%s' is ambiguous\n", cmd);
2342                         return;
2343                 }
2344                 fprintf(fp, "function: %s\n", xcp->comment);
2345                 printusage(xcp, fp);
2346         }
2347 }
2348
2349
2350 /*
2351  * helpsort - do hostname qsort comparisons
2352  */
2353 static int
2354 helpsort(
2355         const void *t1,
2356         const void *t2
2357         )
2358 {
2359         const char * const *    name1 = t1;
2360         const char * const *    name2 = t2;
2361
2362         return strcmp(*name1, *name2);
2363 }
2364
2365
2366 /*
2367  * printusage - print usage information for a command
2368  */
2369 static void
2370 printusage(
2371         struct xcmd *xcp,
2372         FILE *fp
2373         )
2374 {
2375         register int i;
2376
2377         /* XXX: Do we need to warn about extra args here too? */
2378
2379         (void) fprintf(fp, "usage: %s", xcp->keyword);
2380         for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
2381                 if (xcp->arg[i] & OPT)
2382                     (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
2383                 else
2384                     (void) fprintf(fp, " %s", xcp->desc[i]);
2385         }
2386         (void) fprintf(fp, "\n");
2387 }
2388
2389
2390 /*
2391  * timeout - set time out time
2392  */
2393 static void
2394 timeout(
2395         struct parse *pcmd,
2396         FILE *fp
2397         )
2398 {
2399         int val;
2400
2401         if (pcmd->nargs == 0) {
2402                 val = (int)tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
2403                 (void) fprintf(fp, "primary timeout %d ms\n", val);
2404         } else {
2405                 tvout.tv_sec = pcmd->argval[0].uval / 1000;
2406                 tvout.tv_usec = (pcmd->argval[0].uval - ((long)tvout.tv_sec * 1000))
2407                         * 1000;
2408         }
2409 }
2410
2411
2412 /*
2413  * auth_delay - set delay for auth requests
2414  */
2415 static void
2416 auth_delay(
2417         struct parse *pcmd,
2418         FILE *fp
2419         )
2420 {
2421         int isneg;
2422         u_long val;
2423
2424         if (pcmd->nargs == 0) {
2425                 val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
2426                 (void) fprintf(fp, "delay %lu ms\n", val);
2427         } else {
2428                 if (pcmd->argval[0].ival < 0) {
2429                         isneg = 1;
2430                         val = (u_long)(-pcmd->argval[0].ival);
2431                 } else {
2432                         isneg = 0;
2433                         val = (u_long)pcmd->argval[0].ival;
2434                 }
2435
2436                 delay_time.l_ui = val / 1000;
2437                 val %= 1000;
2438                 delay_time.l_uf = val * 4294967;        /* 2**32/1000 */
2439
2440                 if (isneg)
2441                     L_NEG(&delay_time);
2442         }
2443 }
2444
2445
2446 /*
2447  * host - set the host we are dealing with.
2448  */
2449 static void
2450 host(
2451         struct parse *pcmd,
2452         FILE *fp
2453         )
2454 {
2455         int i;
2456
2457         if (pcmd->nargs == 0) {
2458                 if (havehost)
2459                         (void) fprintf(fp, "current host is %s\n",
2460                                            currenthost);
2461                 else
2462                         (void) fprintf(fp, "no current host\n");
2463                 return;
2464         }
2465
2466         i = 0;
2467         ai_fam_templ = ai_fam_default;
2468         if (pcmd->nargs == 2) {
2469                 if (!strcmp("-4", pcmd->argval[i].string))
2470                         ai_fam_templ = AF_INET;
2471                 else if (!strcmp("-6", pcmd->argval[i].string))
2472                         ai_fam_templ = AF_INET6;
2473                 else
2474                         goto no_change;
2475                 i = 1;
2476         }
2477         if (openhost(pcmd->argval[i].string, ai_fam_templ)) {
2478                 fprintf(fp, "current host set to %s\n", currenthost);
2479         } else {
2480     no_change:
2481                 if (havehost)
2482                         fprintf(fp, "current host remains %s\n",
2483                                 currenthost);
2484                 else
2485                         fprintf(fp, "still no current host\n");
2486         }
2487 }
2488
2489
2490 /*
2491  * poll - do one (or more) polls of the host via NTP
2492  */
2493 /*ARGSUSED*/
2494 static void
2495 ntp_poll(
2496         struct parse *pcmd,
2497         FILE *fp
2498         )
2499 {
2500         (void) fprintf(fp, "poll not implemented yet\n");
2501 }
2502
2503
2504 /*
2505  * showdrefid2str - return a string explanation of the value of drefid
2506  */
2507 static char *
2508 showdrefid2str(void)
2509 {
2510         switch (drefid) {
2511             case REFID_HASH:
2512                 return "hash";
2513             case REFID_IPV4:
2514                 return "ipv4";
2515             default:
2516                 return "Unknown";
2517         }
2518 }
2519
2520
2521 /*
2522  * drefid - display/change "display hash" 
2523  */
2524 static void
2525 showdrefid(
2526         struct parse *pcmd,
2527         FILE *fp
2528         )
2529 {
2530         if (pcmd->nargs == 0) {
2531                 (void) fprintf(fp, "drefid value is %s\n", showdrefid2str());
2532                 return;
2533         } else if (STREQ(pcmd->argval[0].string, "hash")) {
2534                 drefid = REFID_HASH;
2535         } else if (STREQ(pcmd->argval[0].string, "ipv4")) {
2536                 drefid = REFID_IPV4;
2537         } else {
2538                 (void) fprintf(fp, "What?\n");
2539                 return;
2540         }
2541         (void) fprintf(fp, "drefid value set to %s\n", showdrefid2str());
2542 }
2543
2544
2545 /*
2546  * keyid - get a keyid to use for authenticating requests
2547  */
2548 static void
2549 keyid(
2550         struct parse *pcmd,
2551         FILE *fp
2552         )
2553 {
2554         if (pcmd->nargs == 0) {
2555                 if (info_auth_keyid == 0)
2556                     (void) fprintf(fp, "no keyid defined\n");
2557                 else
2558                     (void) fprintf(fp, "keyid is %lu\n", (u_long)info_auth_keyid);
2559         } else {
2560                 /* allow zero so that keyid can be cleared. */
2561                 if(pcmd->argval[0].uval > NTP_MAXKEY)
2562                     (void) fprintf(fp, "Invalid key identifier\n");
2563                 info_auth_keyid = pcmd->argval[0].uval;
2564         }
2565 }
2566
2567 /*
2568  * keytype - get type of key to use for authenticating requests
2569  */
2570 static void
2571 keytype(
2572         struct parse *pcmd,
2573         FILE *fp
2574         )
2575 {
2576         const char *    digest_name;
2577         size_t          digest_len;
2578         int             key_type;
2579
2580         if (!pcmd->nargs) {
2581                 fprintf(fp, "keytype is %s with %lu octet digests\n",
2582                         keytype_name(info_auth_keytype),
2583                         (u_long)info_auth_hashlen);
2584                 return;
2585         }
2586
2587         digest_name = pcmd->argval[0].string;
2588         digest_len = 0;
2589         key_type = keytype_from_text(digest_name, &digest_len);
2590
2591         if (!key_type) {
2592                 fprintf(fp, "keytype is not valid. "
2593 #ifdef OPENSSL
2594                         "Type \"help keytype\" for the available digest types.\n");
2595 #else
2596                         "Only \"md5\" is available.\n");
2597 #endif
2598                 return;
2599         }
2600
2601         info_auth_keytype = key_type;
2602         info_auth_hashlen = digest_len;
2603 }
2604
2605
2606 /*
2607  * passwd - get an authentication key
2608  */
2609 /*ARGSUSED*/
2610 static void
2611 passwd(
2612         struct parse *pcmd,
2613         FILE *fp
2614         )
2615 {
2616         const char *pass;
2617
2618         if (info_auth_keyid == 0) {
2619                 info_auth_keyid = getkeyid("Keyid: ");
2620                 if (info_auth_keyid == 0) {
2621                         (void)fprintf(fp, "Keyid must be defined\n");
2622                         return;
2623                 }
2624         }
2625         if (pcmd->nargs >= 1)
2626                 pass = pcmd->argval[0].string;
2627         else {
2628                 pass = getpass_keytype(info_auth_keytype);
2629                 if ('\0' == pass[0]) {
2630                         fprintf(fp, "Password unchanged\n");
2631                         return;
2632                 }
2633         }
2634         authusekey(info_auth_keyid, info_auth_keytype,
2635                    (const u_char *)pass);
2636         authtrust(info_auth_keyid, 1);
2637 }
2638
2639
2640 /*
2641  * hostnames - set the showhostnames flag
2642  */
2643 static void
2644 hostnames(
2645         struct parse *pcmd,
2646         FILE *fp
2647         )
2648 {
2649         if (pcmd->nargs == 0) {
2650                 if (showhostnames)
2651                     (void) fprintf(fp, "hostnames being shown\n");
2652                 else
2653                     (void) fprintf(fp, "hostnames not being shown\n");
2654         } else {
2655                 if (STREQ(pcmd->argval[0].string, "yes"))
2656                     showhostnames = 1;
2657                 else if (STREQ(pcmd->argval[0].string, "no"))
2658                     showhostnames = 0;
2659                 else
2660                     (void)fprintf(stderr, "What?\n");
2661         }
2662 }
2663
2664
2665
2666 /*
2667  * setdebug - set/change debugging level
2668  */
2669 static void
2670 setdebug(
2671         struct parse *pcmd,
2672         FILE *fp
2673         )
2674 {
2675         if (pcmd->nargs == 0) {
2676                 (void) fprintf(fp, "debug level is %d\n", debug);
2677                 return;
2678         } else if (STREQ(pcmd->argval[0].string, "no")) {
2679                 debug = 0;
2680         } else if (STREQ(pcmd->argval[0].string, "more")) {
2681                 debug++;
2682         } else if (STREQ(pcmd->argval[0].string, "less")) {
2683                 debug--;
2684         } else {
2685                 (void) fprintf(fp, "What?\n");
2686                 return;
2687         }
2688         (void) fprintf(fp, "debug level set to %d\n", debug);
2689 }
2690
2691
2692 /*
2693  * quit - stop this nonsense
2694  */
2695 /*ARGSUSED*/
2696 static void
2697 quit(
2698         struct parse *pcmd,
2699         FILE *fp
2700         )
2701 {
2702         if (havehost)
2703             closesocket(sockfd);        /* cleanliness next to godliness */
2704         exit(0);
2705 }
2706
2707
2708 /*
2709  * version - print the current version number
2710  */
2711 /*ARGSUSED*/
2712 static void
2713 version(
2714         struct parse *pcmd,
2715         FILE *fp
2716         )
2717 {
2718
2719         (void) fprintf(fp, "%s\n", Version);
2720         return;
2721 }
2722
2723
2724 /*
2725  * raw - set raw mode output
2726  */
2727 /*ARGSUSED*/
2728 static void
2729 raw(
2730         struct parse *pcmd,
2731         FILE *fp
2732         )
2733 {
2734         rawmode = 1;
2735         (void) fprintf(fp, "Output set to raw\n");
2736 }
2737
2738
2739 /*
2740  * cooked - set cooked mode output
2741  */
2742 /*ARGSUSED*/
2743 static void
2744 cooked(
2745         struct parse *pcmd,
2746         FILE *fp
2747         )
2748 {
2749         rawmode = 0;
2750         (void) fprintf(fp, "Output set to cooked\n");
2751         return;
2752 }
2753
2754
2755 /*
2756  * authenticate - always authenticate requests to this host
2757  */
2758 static void
2759 authenticate(
2760         struct parse *pcmd,
2761         FILE *fp
2762         )
2763 {
2764         if (pcmd->nargs == 0) {
2765                 if (always_auth) {
2766                         (void) fprintf(fp,
2767                                        "authenticated requests being sent\n");
2768                 } else
2769                     (void) fprintf(fp,
2770                                    "unauthenticated requests being sent\n");
2771         } else {
2772                 if (STREQ(pcmd->argval[0].string, "yes")) {
2773                         always_auth = 1;
2774                 } else if (STREQ(pcmd->argval[0].string, "no")) {
2775                         always_auth = 0;
2776                 } else
2777                     (void)fprintf(stderr, "What?\n");
2778         }
2779 }
2780
2781
2782 /*
2783  * ntpversion - choose the NTP version to use
2784  */
2785 static void
2786 ntpversion(
2787         struct parse *pcmd,
2788         FILE *fp
2789         )
2790 {
2791         if (pcmd->nargs == 0) {
2792                 (void) fprintf(fp,
2793                                "NTP version being claimed is %d\n", pktversion);
2794         } else {
2795                 if (pcmd->argval[0].uval < NTP_OLDVERSION
2796                     || pcmd->argval[0].uval > NTP_VERSION) {
2797                         (void) fprintf(stderr, "versions %d to %d, please\n",
2798                                        NTP_OLDVERSION, NTP_VERSION);
2799                 } else {
2800                         pktversion = (u_char) pcmd->argval[0].uval;
2801                 }
2802         }
2803 }
2804
2805
2806 static void __attribute__((__format__(__printf__, 1, 0)))
2807 vwarning(const char *fmt, va_list ap)
2808 {
2809         int serrno = errno;
2810         (void) fprintf(stderr, "%s: ", progname);
2811         vfprintf(stderr, fmt, ap);
2812         (void) fprintf(stderr, ": %s\n", strerror(serrno));
2813 }
2814
2815 /*
2816  * warning - print a warning message
2817  */
2818 static void __attribute__((__format__(__printf__, 1, 2)))
2819 warning(
2820         const char *fmt,
2821         ...
2822         )
2823 {
2824         va_list ap;
2825         va_start(ap, fmt);
2826         vwarning(fmt, ap);
2827         va_end(ap);
2828 }
2829
2830
2831 /*
2832  * error - print a message and exit
2833  */
2834 static void __attribute__((__format__(__printf__, 1, 2)))
2835 error(
2836         const char *fmt,
2837         ...
2838         )
2839 {
2840         va_list ap;
2841         va_start(ap, fmt);
2842         vwarning(fmt, ap);
2843         va_end(ap);
2844         exit(1);
2845 }
2846 /*
2847  * getkeyid - prompt the user for a keyid to use
2848  */
2849 static u_long
2850 getkeyid(
2851         const char *keyprompt
2852         )
2853 {
2854         int c;
2855         FILE *fi;
2856         char pbuf[20];
2857         size_t i;
2858         size_t ilim;
2859
2860 #ifndef SYS_WINNT
2861         if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
2862 #else
2863         if ((fi = _fdopen(open("CONIN$", _O_TEXT), "r")) == NULL)
2864 #endif /* SYS_WINNT */
2865                 fi = stdin;
2866         else
2867                 setbuf(fi, (char *)NULL);
2868         fprintf(stderr, "%s", keyprompt); fflush(stderr);
2869         for (i = 0, ilim = COUNTOF(pbuf) - 1;
2870              i < ilim && (c = getc(fi)) != '\n' && c != EOF;
2871              )
2872                 pbuf[i++] = (char)c;
2873         pbuf[i] = '\0';
2874         if (fi != stdin)
2875                 fclose(fi);
2876
2877         return (u_long) atoi(pbuf);
2878 }
2879
2880
2881 /*
2882  * atoascii - printable-ize possibly ascii data using the character
2883  *            transformations cat -v uses.
2884  */
2885 static void
2886 atoascii(
2887         const char *in,
2888         size_t in_octets,
2889         char *out,
2890         size_t out_octets
2891         )
2892 {
2893         const u_char *  pchIn;
2894         const u_char *  pchInLimit;
2895         u_char *        pchOut;
2896         u_char          c;
2897
2898         pchIn = (const u_char *)in;
2899         pchInLimit = pchIn + in_octets;
2900         pchOut = (u_char *)out;
2901
2902         if (NULL == pchIn) {
2903                 if (0 < out_octets)
2904                         *pchOut = '\0';
2905                 return;
2906         }
2907
2908 #define ONEOUT(c)                                       \
2909 do {                                                    \
2910         if (0 == --out_octets) {                        \
2911                 *pchOut = '\0';                         \
2912                 return;                                 \
2913         }                                               \
2914         *pchOut++ = (c);                                \
2915 } while (0)
2916
2917         for (   ; pchIn < pchInLimit; pchIn++) {
2918                 c = *pchIn;
2919                 if ('\0' == c)
2920                         break;
2921                 if (c & 0x80) {
2922                         ONEOUT('M');
2923                         ONEOUT('-');
2924                         c &= 0x7f;
2925                 }
2926                 if (c < ' ') {
2927                         ONEOUT('^');
2928                         ONEOUT((u_char)(c + '@'));
2929                 } else if (0x7f == c) {
2930                         ONEOUT('^');
2931                         ONEOUT('?');
2932                 } else
2933                         ONEOUT(c);
2934         }
2935         ONEOUT('\0');
2936
2937 #undef ONEOUT
2938 }
2939
2940
2941 /*
2942  * makeascii - print possibly ascii data using the character
2943  *             transformations that cat -v uses.
2944  */
2945 void
2946 makeascii(
2947         size_t length,
2948         const char *data,
2949         FILE *fp
2950         )
2951 {
2952         const u_char *data_u_char;
2953         const u_char *cp;
2954         int c;
2955
2956         data_u_char = (const u_char *)data;
2957
2958         for (cp = data_u_char; cp < data_u_char + length; cp++) {
2959                 c = (int)*cp;
2960                 if (c & 0x80) {
2961                         putc('M', fp);
2962                         putc('-', fp);
2963                         c &= 0x7f;
2964                 }
2965
2966                 if (c < ' ') {
2967                         putc('^', fp);
2968                         putc(c + '@', fp);
2969                 } else if (0x7f == c) {
2970                         putc('^', fp);
2971                         putc('?', fp);
2972                 } else
2973                         putc(c, fp);
2974         }
2975 }
2976
2977
2978 /*
2979  * asciize - same thing as makeascii except add a newline
2980  */
2981 void
2982 asciize(
2983         int length,
2984         char *data,
2985         FILE *fp
2986         )
2987 {
2988         makeascii(length, data, fp);
2989         putc('\n', fp);
2990 }
2991
2992
2993 /*
2994  * truncate string to fit clipping excess at end.
2995  *      "too long"      ->      "too l"
2996  * Used for hostnames.
2997  */
2998 const char *
2999 trunc_right(
3000         const char *    src,
3001         size_t          width
3002         )
3003 {
3004         size_t  sl;
3005         char *  out;
3006
3007
3008         sl = strlen(src);
3009         if (sl > width && LIB_BUFLENGTH - 1 > width && width > 0) {
3010                 LIB_GETBUF(out);
3011                 memcpy(out, src, width);
3012                 out[width] = '\0';
3013
3014                 return out;
3015         }
3016
3017         return src;
3018 }
3019
3020
3021 /*
3022  * truncate string to fit by preserving right side and using '_' to hint
3023  *      "too long"      ->      "_long"
3024  * Used for local IPv6 addresses, where low bits differentiate.
3025  */
3026 const char *
3027 trunc_left(
3028         const char *    src,
3029         size_t          width
3030         )
3031 {
3032         size_t  sl;
3033         char *  out;
3034
3035
3036         sl = strlen(src);
3037         if (sl > width && LIB_BUFLENGTH - 1 > width && width > 1) {
3038                 LIB_GETBUF(out);
3039                 out[0] = '_';
3040                 memcpy(&out[1], &src[sl + 1 - width], width);
3041
3042                 return out;
3043         }
3044
3045         return src;
3046 }
3047
3048
3049 /*
3050  * Some circular buffer space
3051  */
3052 #define CBLEN   80
3053 #define NUMCB   6
3054
3055 char circ_buf[NUMCB][CBLEN];
3056 int nextcb = 0;
3057
3058 /*
3059  * nextvar - find the next variable in the buffer
3060  */
3061 int
3062 nextvar(
3063         size_t *datalen,
3064         const char **datap,
3065         char **vname,
3066         char **vvalue
3067         )
3068 {
3069         const char *cp;
3070         const char *np;
3071         const char *cpend;
3072         size_t srclen;
3073         size_t len;
3074         static char name[MAXVARLEN];
3075         static char value[MAXVALLEN];
3076
3077         cp = *datap;
3078         cpend = cp + *datalen;
3079
3080         /*
3081          * Space past commas and white space
3082          */
3083         while (cp < cpend && (*cp == ',' || isspace(pgetc(cp))))
3084                 cp++;
3085         if (cp >= cpend)
3086                 return 0;
3087
3088         /*
3089          * Copy name until we hit a ',', an '=', a '\r' or a '\n'.  Backspace
3090          * over any white space and terminate it.
3091          */
3092         srclen = strcspn(cp, ",=\r\n");
3093         srclen = min(srclen, (size_t)(cpend - cp));
3094         len = srclen;
3095         while (len > 0 && isspace(pgetc(&cp[len - 1])))
3096                 len--;
3097         if (len >= sizeof(name))
3098             return 0;
3099         if (len > 0)
3100                 memcpy(name, cp, len);
3101         name[len] = '\0';
3102         *vname = name;
3103         cp += srclen;
3104
3105         /*
3106          * Check if we hit the end of the buffer or a ','.  If so we are done.
3107          */
3108         if (cp >= cpend || *cp == ',' || *cp == '\r' || *cp == '\n') {
3109                 if (cp < cpend)
3110                         cp++;
3111                 *datap = cp;
3112                 *datalen = size2int_sat(cpend - cp);
3113                 *vvalue = NULL;
3114                 return 1;
3115         }
3116
3117         /*
3118          * So far, so good.  Copy out the value
3119          */
3120         cp++;   /* past '=' */
3121         while (cp < cpend && (isspace(pgetc(cp)) && *cp != '\r' && *cp != '\n'))
3122                 cp++;
3123         np = cp;
3124         if ('"' == *np) {
3125                 do {
3126                         np++;
3127                 } while (np < cpend && '"' != *np);
3128                 if (np < cpend && '"' == *np)
3129                         np++;
3130         } else {
3131                 while (np < cpend && ',' != *np && '\r' != *np)
3132                         np++;
3133         }
3134         len = np - cp;
3135         if (np > cpend || len >= sizeof(value) ||
3136             (np < cpend && ',' != *np && '\r' != *np))
3137                 return 0;
3138         memcpy(value, cp, len);
3139         /*
3140          * Trim off any trailing whitespace
3141          */
3142         while (len > 0 && isspace(pgetc(&value[len - 1])))
3143                 len--;
3144         value[len] = '\0';
3145
3146         /*
3147          * Return this.  All done.
3148          */
3149         if (np < cpend && ',' == *np)
3150                 np++;
3151         *datap = np;
3152         *datalen = size2int_sat(cpend - np);
3153         *vvalue = value;
3154         return 1;
3155 }
3156
3157
3158 u_short
3159 varfmt(const char * varname)
3160 {
3161         u_int n;
3162
3163         for (n = 0; n < COUNTOF(cookedvars); n++)
3164                 if (!strcmp(varname, cookedvars[n].varname))
3165                         return cookedvars[n].fmt;
3166
3167         return PADDING;
3168 }
3169
3170
3171 /*
3172  * printvars - print variables returned in response packet
3173  */
3174 void
3175 printvars(
3176         size_t length,
3177         const char *data,
3178         int status,
3179         int sttype,
3180         int quiet,
3181         FILE *fp
3182         )
3183 {
3184         if (rawmode)
3185             rawprint(sttype, length, data, status, quiet, fp);
3186         else
3187             cookedprint(sttype, length, data, status, quiet, fp);
3188 }
3189
3190
3191 /*
3192  * rawprint - do a printout of the data in raw mode
3193  */
3194 static void
3195 rawprint(
3196         int datatype,
3197         size_t length,
3198         const char *data,
3199         int status,
3200         int quiet,
3201         FILE *fp
3202         )
3203 {
3204         const char *cp;
3205         const char *cpend;
3206
3207         /*
3208          * Essentially print the data as is.  We reformat unprintables, though.
3209          */
3210         cp = data;
3211         cpend = data + length;
3212
3213         if (!quiet)
3214                 (void) fprintf(fp, "status=0x%04x,\n", status);
3215
3216         while (cp < cpend) {
3217                 if (*cp == '\r') {
3218                         /*
3219                          * If this is a \r and the next character is a
3220                          * \n, supress this, else pretty print it.  Otherwise
3221                          * just output the character.
3222                          */
3223                         if (cp == (cpend - 1) || *(cp + 1) != '\n')
3224                             makeascii(1, cp, fp);
3225                 } else if (isspace(pgetc(cp)) || isprint(pgetc(cp)))
3226                         putc(*cp, fp);
3227                 else
3228                         makeascii(1, cp, fp);
3229                 cp++;
3230         }
3231 }
3232
3233
3234 /*
3235  * Global data used by the cooked output routines
3236  */
3237 int out_chars;          /* number of characters output */
3238 int out_linecount;      /* number of characters output on this line */
3239
3240
3241 /*
3242  * startoutput - get ready to do cooked output
3243  */
3244 static void
3245 startoutput(void)
3246 {
3247         out_chars = 0;
3248         out_linecount = 0;
3249 }
3250
3251
3252 /*
3253  * output - output a variable=value combination
3254  */
3255 static void
3256 output(
3257         FILE *fp,
3258         const char *name,
3259         const char *value
3260         )
3261 {
3262         int len;
3263
3264         /* strlen of "name=value" */
3265         len = size2int_sat(strlen(name) + 1 + strlen(value));
3266
3267         if (out_chars != 0) {
3268                 out_chars += 2;
3269                 if ((out_linecount + len + 2) > MAXOUTLINE) {
3270                         fputs(",\n", fp);
3271                         out_linecount = 0;
3272                 } else {
3273                         fputs(", ", fp);
3274                         out_linecount += 2;
3275                 }
3276         }
3277
3278         fputs(name, fp);
3279         putc('=', fp);
3280         fputs(value, fp);
3281         out_chars += len;
3282         out_linecount += len;
3283 }
3284
3285
3286 /*
3287  * endoutput - terminate a block of cooked output
3288  */
3289 static void
3290 endoutput(
3291         FILE *fp
3292         )
3293 {
3294         if (out_chars != 0)
3295                 putc('\n', fp);
3296 }
3297
3298
3299 /*
3300  * outputarr - output an array of values
3301  */
3302 static void
3303 outputarr(
3304         FILE *fp,
3305         char *name,
3306         int narr,
3307         l_fp *lfp
3308         )
3309 {
3310         char *bp;
3311         char *cp;
3312         size_t i;
3313         size_t len;
3314         char buf[256];
3315
3316         bp = buf;
3317         /*
3318          * Hack to align delay and offset values
3319          */
3320         for (i = (int)strlen(name); i < 11; i++)
3321             *bp++ = ' ';
3322
3323         for (i = narr; i > 0; i--) {
3324                 if (i != narr)
3325                     *bp++ = ' ';
3326                 cp = lfptoms(lfp, 2);
3327                 len = strlen(cp);
3328                 if (len > 7) {
3329                         cp[7] = '\0';
3330                         len = 7;
3331                 }
3332                 while (len < 7) {
3333                         *bp++ = ' ';
3334                         len++;
3335                 }
3336                 while (*cp != '\0')
3337                     *bp++ = *cp++;
3338                 lfp++;
3339         }
3340         *bp = '\0';
3341         output(fp, name, buf);
3342 }
3343
3344 static char *
3345 tstflags(
3346         u_long val
3347         )
3348 {
3349         register char *cp, *s;
3350         size_t cb;
3351         register int i;
3352         register const char *sep;
3353
3354         sep = "";
3355         s = cp = circ_buf[nextcb];
3356         if (++nextcb >= NUMCB)
3357                 nextcb = 0;
3358         cb = sizeof(circ_buf[0]);
3359
3360         snprintf(cp, cb, "%02lx", val);
3361         cp += strlen(cp);
3362         cb -= strlen(cp);
3363         if (!val) {
3364                 strlcat(cp, " ok", cb);
3365                 cp += strlen(cp);
3366                 cb -= strlen(cp);
3367         } else {
3368                 if (cb) {
3369                         *cp++ = ' ';
3370                         cb--;
3371                 }
3372                 for (i = 0; i < (int)COUNTOF(tstflagnames); i++) {
3373                         if (val & 0x1) {
3374                                 snprintf(cp, cb, "%s%s", sep,
3375                                          tstflagnames[i]);
3376                                 sep = ", ";
3377                                 cp += strlen(cp);
3378                                 cb -= strlen(cp);
3379                         }
3380                         val >>= 1;
3381                 }
3382         }
3383         if (cb)
3384                 *cp = '\0';
3385
3386         return s;
3387 }
3388
3389 /*
3390  * cookedprint - output variables in cooked mode
3391  */
3392 static void
3393 cookedprint(
3394         int datatype,
3395         size_t length,
3396         const char *data,
3397         int status,
3398         int quiet,
3399         FILE *fp
3400         )
3401 {
3402         char *name;
3403         char *value;
3404         char output_raw;
3405         int fmt;
3406         l_fp lfp;
3407         sockaddr_u hval;
3408         u_long uval;
3409         int narr;
3410         size_t len;
3411         l_fp lfparr[8];
3412         char b[12];
3413         char bn[2 * MAXVARLEN];
3414         char bv[2 * MAXVALLEN];
3415
3416         UNUSED_ARG(datatype);
3417
3418         if (!quiet)
3419                 fprintf(fp, "status=%04x %s,\n", status,
3420                         statustoa(datatype, status));
3421
3422         startoutput();
3423         while (nextvar(&length, &data, &name, &value)) {
3424                 fmt = varfmt(name);
3425                 output_raw = 0;
3426                 switch (fmt) {
3427
3428                 case PADDING:
3429                         output_raw = '*';
3430                         break;
3431
3432                 case TS:
3433                         if (!value || !decodets(value, &lfp))
3434                                 output_raw = '?';
3435                         else
3436                                 output(fp, name, prettydate(&lfp));
3437                         break;
3438
3439                 case HA:        /* fallthru */
3440                 case NA:
3441                         if (!value || !decodenetnum(value, &hval)) {
3442                                 output_raw = '?';
3443                         } else if (fmt == HA){
3444                                 output(fp, name, nntohost(&hval));
3445                         } else {
3446                                 output(fp, name, stoa(&hval));
3447                         }
3448                         break;
3449
3450                 case RF:
3451                         if (!value) {
3452                                 output_raw = '?';
3453                         } else if (decodenetnum(value, &hval)) {
3454                                 if (ISREFCLOCKADR(&hval))
3455                                         output(fp, name,
3456                                                refnumtoa(&hval));
3457                                 else
3458                                         output(fp, name, stoa(&hval));
3459                         } else if (strlen(value) <= 4) {
3460                                 output(fp, name, value);
3461                         } else {
3462                                 output_raw = '?';
3463                         }
3464                         break;
3465
3466                 case LP:
3467                         if (!value || !decodeuint(value, &uval) || uval > 3) {
3468                                 output_raw = '?';
3469                         } else {
3470                                 b[0] = (0x2 & uval)
3471                                            ? '1'
3472                                            : '0';
3473                                 b[1] = (0x1 & uval)
3474                                            ? '1'
3475                                            : '0';
3476                                 b[2] = '\0';
3477                                 output(fp, name, b);
3478                         }
3479                         break;
3480
3481                 case OC:
3482                         if (!value || !decodeuint(value, &uval)) {
3483                                 output_raw = '?';
3484                         } else {
3485                                 snprintf(b, sizeof(b), "%03lo", uval);
3486                                 output(fp, name, b);
3487                         }
3488                         break;
3489
3490                 case AR:
3491                         if (!value || !decodearr(value, &narr, lfparr, 8))
3492                                 output_raw = '?';
3493                         else
3494                                 outputarr(fp, name, narr, lfparr);
3495                         break;
3496
3497                 case FX:
3498                         if (!value || !decodeuint(value, &uval))
3499                                 output_raw = '?';
3500                         else
3501                                 output(fp, name, tstflags(uval));
3502                         break;
3503
3504                 default:
3505                         fprintf(stderr, "Internal error in cookedprint, %s=%s, fmt %d\n",
3506                                 name, value, fmt);
3507                         output_raw = '?';
3508                         break;
3509                 }
3510
3511                 if (output_raw != 0) {
3512                         /* TALOS-CAN-0063: avoid buffer overrun */
3513                         atoascii(name, MAXVARLEN, bn, sizeof(bn));
3514                         if (output_raw != '*') {
3515                                 atoascii(value, MAXVALLEN,
3516                                          bv, sizeof(bv) - 1);
3517                                 len = strlen(bv);
3518                                 bv[len] = output_raw;
3519                                 bv[len+1] = '\0';
3520                         } else {
3521                                 atoascii(value, MAXVALLEN,
3522                                          bv, sizeof(bv));
3523                         }
3524                         output(fp, bn, bv);
3525                 }
3526         }
3527         endoutput(fp);
3528 }
3529
3530
3531 /*
3532  * sortassoc - sort associations in the cache into ascending order
3533  */
3534 void
3535 sortassoc(void)
3536 {
3537         if (numassoc > 1)
3538                 qsort(assoc_cache, (size_t)numassoc,
3539                       sizeof(assoc_cache[0]), &assoccmp);
3540 }
3541
3542
3543 /*
3544  * assoccmp - compare two associations
3545  */
3546 static int
3547 assoccmp(
3548         const void *t1,
3549         const void *t2
3550         )
3551 {
3552         const struct association *ass1 = t1;
3553         const struct association *ass2 = t2;
3554
3555         if (ass1->assid < ass2->assid)
3556                 return -1;
3557         if (ass1->assid > ass2->assid)
3558                 return 1;
3559         return 0;
3560 }
3561
3562
3563 /*
3564  * grow_assoc_cache() - enlarge dynamic assoc_cache array
3565  *
3566  * The strategy is to add an assumed 4k page size at a time, leaving
3567  * room for malloc() bookkeeping overhead equivalent to 4 pointers.
3568  */
3569 void
3570 grow_assoc_cache(void)
3571 {
3572         static size_t   prior_sz;
3573         size_t          new_sz;
3574
3575         new_sz = prior_sz + 4 * 1024;
3576         if (0 == prior_sz) {
3577                 new_sz -= 4 * sizeof(void *);
3578         }
3579         assoc_cache = erealloc_zero(assoc_cache, new_sz, prior_sz); 
3580         prior_sz = new_sz;
3581         assoc_cache_slots = (u_int)(new_sz / sizeof(assoc_cache[0]));
3582 }
3583
3584
3585 /*
3586  * ntpq_custom_opt_handler - autoopts handler for -c and -p
3587  *
3588  * By default, autoopts loses the relative order of -c and -p options
3589  * on the command line.  This routine replaces the default handler for
3590  * those routines and builds a list of commands to execute preserving
3591  * the order.
3592  */
3593 void
3594 ntpq_custom_opt_handler(
3595         tOptions *pOptions,
3596         tOptDesc *pOptDesc
3597         )
3598 {
3599         switch (pOptDesc->optValue) {
3600
3601         default:
3602                 fprintf(stderr,
3603                         "ntpq_custom_opt_handler unexpected option '%c' (%d)\n",
3604                         pOptDesc->optValue, pOptDesc->optValue);
3605                 exit(1);
3606
3607         case 'c':
3608                 ADDCMD(pOptDesc->pzLastArg);
3609                 break;
3610
3611         case 'p':
3612                 ADDCMD("peers");
3613                 break;
3614         }
3615 }
3616 /*
3617  * Obtain list of digest names
3618  */
3619
3620 #if defined(OPENSSL) && !defined(HAVE_EVP_MD_DO_ALL_SORTED)
3621 # if defined(_MSC_VER) && OPENSSL_VERSION_NUMBER >= 0x10100000L
3622 #  define HAVE_EVP_MD_DO_ALL_SORTED
3623 # endif
3624 #endif
3625
3626 #ifdef OPENSSL
3627 # ifdef HAVE_EVP_MD_DO_ALL_SORTED
3628 #  define K_PER_LINE    8
3629 #  define K_NL_PFX_STR  "\n    "
3630 #  define K_DELIM_STR   ", "
3631
3632 struct hstate {
3633    char *list;
3634    const char **seen;
3635    int idx;
3636 };
3637
3638
3639 static void
3640 list_md_fn(const EVP_MD *m, const char *from, const char *to, void *arg)
3641 {
3642     size_t        len, n, digest_len;
3643     const char    *name, **seen;
3644     struct hstate *hstate = arg;
3645     char          *cp;
3646
3647     /* m is MD obj, from is name or alias, to is base name for alias */
3648     if (!m || !from || to) {
3649         return; /* Ignore aliases */
3650     }
3651
3652     /* Discard MACs that NTP won't accept. */
3653     /* Keep this consistent with keytype_from_text() in ssl_init.c. */
3654     if (EVP_MD_size(m) > (MAX_MAC_LEN - sizeof(keyid_t))) {
3655         return;
3656     }
3657
3658     name = EVP_MD_name(m);
3659
3660     /* Lowercase names aren't accepted by keytype_from_text in ssl_init.c */
3661
3662     for (cp = name; *cp; cp++) {
3663         if (islower((unsigned char)*cp)) {
3664             return;
3665         }
3666     }
3667
3668     len = (cp - name) + 1;
3669
3670     /* There are duplicates.  Discard if name has been seen. */
3671
3672     for (seen = hstate->seen; *seen; seen++) {
3673         if (!strcmp(*seen, name)) {
3674             return;
3675         }
3676     }
3677
3678     n = (seen - hstate->seen) + 2;
3679     hstate->seen = erealloc(hstate->seen, n * sizeof(*seen));
3680     hstate->seen[n-2] = name;
3681     hstate->seen[n-1] = NULL;
3682
3683     if (hstate->list != NULL) {
3684         len += strlen(hstate->list);
3685     }
3686
3687     len += (hstate->idx >= K_PER_LINE)
3688                 ? strlen(K_NL_PFX_STR)
3689                 : strlen(K_DELIM_STR);
3690
3691     if (hstate->list == NULL) {
3692         hstate->list = (char *)emalloc(len);
3693         hstate->list[0] = '\0';
3694     } else {
3695         hstate->list = (char *)erealloc(hstate->list, len);
3696     }
3697
3698     sprintf(hstate->list + strlen(hstate->list), "%s%s",
3699             ((hstate->idx >= K_PER_LINE) ? K_NL_PFX_STR : K_DELIM_STR),
3700             name);
3701
3702     if (hstate->idx >= K_PER_LINE) {
3703         hstate->idx = 1;
3704     } else {
3705         hstate->idx++;
3706     }
3707 }
3708
3709
3710 /* Insert CMAC into SSL digests list */
3711 static char *
3712 insert_cmac(char *list)
3713 {
3714     int insert;
3715     size_t len;
3716
3717
3718     /* If list empty, we need to insert CMAC on new line */
3719     insert = (!list || !*list);
3720
3721     if (insert) {
3722         len = strlen(K_NL_PFX_STR) + strlen(CMAC);
3723         list = (char *)erealloc(list, len + 1);
3724         sprintf(list, "%s%s", K_NL_PFX_STR, CMAC);
3725     } else {    /* List not empty */
3726         /* Check if CMAC already in list - future proofing */
3727         const char *cmac_sn;
3728         char *cmac_p;
3729
3730         cmac_sn = OBJ_nid2sn(NID_cmac);
3731         cmac_p = list;
3732         insert = cmac_sn != NULL && *cmac_sn != '\0';
3733
3734         /* CMAC in list if found, followed by nul char or ',' */
3735         while (insert && NULL != (cmac_p = strstr(cmac_p, cmac_sn))) {
3736             cmac_p += strlen(cmac_sn);
3737             /* Still need to insert if not nul and not ',' */
3738             insert = *cmac_p && ',' != *cmac_p;
3739         }
3740
3741         /* Find proper insertion point */
3742         if (insert) {
3743             char *last_nl;
3744             char *point;
3745             char *delim;
3746             int found;
3747
3748             /* Default to start if list empty */
3749             found = 0;
3750             delim = list;
3751             len = strlen(list);
3752
3753             /* While new lines */
3754             while (delim < list + len && *delim &&
3755                         !strncmp(K_NL_PFX_STR, delim, strlen(K_NL_PFX_STR))) {
3756                 point = delim + strlen(K_NL_PFX_STR);
3757
3758                 /* While digest names on line */
3759                 while (point < list + len && *point) {
3760                     /* Another digest after on same or next line? */
3761                     delim = strstr( point, K_DELIM_STR);
3762                     last_nl = strstr( point, K_NL_PFX_STR);
3763
3764                     /* No - end of list */
3765                     if (!delim && !last_nl) {
3766                         delim = list + len;
3767                     } else
3768                     /* New line and no delim or before delim? */
3769                     if (last_nl && (!delim || last_nl < delim)) {
3770                         delim = last_nl;
3771                     }
3772
3773                     /* Found insertion point where CMAC before entry? */
3774                     if (strncmp(CMAC, point, delim - point) < 0) {
3775                         found = 1;
3776                         break;
3777                     }
3778
3779                     if (delim < list + len && *delim &&
3780                             !strncmp(K_DELIM_STR, delim, strlen(K_DELIM_STR))) {
3781                         point += strlen(K_DELIM_STR);
3782                     } else {
3783                         break;
3784                     }
3785                 } /* While digest names on line */
3786             } /* While new lines */
3787
3788             /* If found in list */
3789             if (found) {
3790                 /* insert cmac and delim */
3791                 /* Space for list could move - save offset */
3792                 ptrdiff_t p_offset = point - list;
3793                 len += strlen(CMAC) + strlen(K_DELIM_STR);
3794                 list = (char *)erealloc(list, len + 1);
3795                 point = list + p_offset;
3796                 /* move to handle src/dest overlap */
3797                 memmove(point + strlen(CMAC) + strlen(K_DELIM_STR),
3798                                         point, strlen(point) + 1);
3799                 strncpy(point, CMAC, strlen(CMAC));
3800                 strncpy(point + strlen(CMAC), K_DELIM_STR, strlen(K_DELIM_STR));
3801             } else {    /* End of list */
3802                 /* append delim and cmac */
3803                 len += strlen(K_DELIM_STR) + strlen(CMAC);
3804                 list = (char *)erealloc(list, len + 1);
3805                 strcpy(list + strlen(list), K_DELIM_STR);
3806                 strcpy(list + strlen(list), CMAC);
3807             }
3808         } /* insert */
3809     } /* List not empty */
3810
3811     return list;
3812 }
3813 # endif
3814 #endif
3815
3816
3817 static char *
3818 list_digest_names(void)
3819 {
3820     char *list = NULL;
3821
3822 #ifdef OPENSSL
3823 # ifdef HAVE_EVP_MD_DO_ALL_SORTED
3824     struct hstate hstate = { NULL, NULL, K_PER_LINE+1 };
3825
3826     /* replace calloc(1, sizeof(const char *)) */
3827     hstate.seen = (const char **)emalloc_zero(sizeof(const char *));
3828
3829     INIT_SSL();
3830     EVP_MD_do_all_sorted(list_md_fn, &hstate);
3831     list = hstate.list;
3832     free(hstate.seen);
3833
3834     list = insert_cmac(list);   /* Insert CMAC into SSL digests list */
3835
3836 # else
3837     list = (char *)emalloc(sizeof("md5, others (upgrade to OpenSSL-1.0 for full list)"));
3838     strcpy(list, "md5, others (upgrade to OpenSSL-1.0 for full list)");
3839 # endif
3840 #else
3841     list = (char *)emalloc(sizeof("md5"));
3842     strcpy(list, "md5");
3843 #endif
3844
3845     return list;
3846 }
3847
3848 #define CTRLC_STACK_MAX 4
3849 static volatile size_t          ctrlc_stack_len = 0;
3850 static volatile Ctrl_C_Handler  ctrlc_stack[CTRLC_STACK_MAX];
3851
3852
3853
3854 int/*BOOL*/
3855 push_ctrl_c_handler(
3856         Ctrl_C_Handler func
3857         )
3858 {
3859         size_t size = ctrlc_stack_len;
3860         if (func && (size < CTRLC_STACK_MAX)) {
3861                 ctrlc_stack[size] = func;
3862                 ctrlc_stack_len = size + 1;
3863                 return TRUE;
3864         }
3865         return FALSE;   
3866 }
3867
3868 int/*BOOL*/
3869 pop_ctrl_c_handler(
3870         Ctrl_C_Handler func
3871         )
3872 {
3873         size_t size = ctrlc_stack_len;
3874         if (size) {
3875                 --size;
3876                 if (func == NULL || func == ctrlc_stack[size]) {
3877                         ctrlc_stack_len = size;
3878                         return TRUE;
3879                 }
3880         }
3881         return FALSE;
3882 }
3883
3884 static void
3885 on_ctrlc(void)
3886 {
3887         size_t size = ctrlc_stack_len;
3888         while (size)
3889                 if ((*ctrlc_stack[--size])())
3890                         break;
3891 }
3892
3893 static int
3894 my_easprintf(
3895         char **         ppinto,
3896         const char *    fmt   ,
3897         ...
3898         )
3899 {
3900         va_list va;
3901         int     prc;
3902         size_t  len = 128;
3903         char *  buf = emalloc(len);
3904
3905   again:
3906         /* Note: we expect the memory allocation to fail long before the
3907          * increment in buffer size actually overflows.
3908          */
3909         buf = (buf) ? erealloc(buf, len) : emalloc(len);
3910
3911         va_start(va, fmt);
3912         prc = vsnprintf(buf, len, fmt, va);
3913         va_end(va);
3914
3915         if (prc < 0) {
3916                 /* might be very old vsnprintf. Or actually MSVC... */
3917                 len += len >> 1;
3918                 goto again;
3919         }
3920         if ((size_t)prc >= len) {
3921                 /* at least we have the proper size now... */
3922                 len = (size_t)prc + 1;
3923                 goto again;
3924         }
3925         if ((size_t)prc < (len - 32))
3926                 buf = erealloc(buf, (size_t)prc + 1);
3927         *ppinto = buf;
3928         return prc;
3929 }