/src/ntp-dev/ntpd/ntp_control.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * ntp_control.c - respond to mode 6 control messages and send async |
3 | | * traps. Provides service to ntpq and others. |
4 | | */ |
5 | | |
6 | | #ifdef HAVE_CONFIG_H |
7 | | # include <config.h> |
8 | | #endif |
9 | | |
10 | | #include <stdio.h> |
11 | | #include <ctype.h> |
12 | | #include <signal.h> |
13 | | #include <sys/stat.h> |
14 | | #ifdef HAVE_NETINET_IN_H |
15 | | # include <netinet/in.h> |
16 | | #endif |
17 | | #include <arpa/inet.h> |
18 | | |
19 | | #include "ntpd.h" |
20 | | #include "ntp_io.h" |
21 | | #include "ntp_refclock.h" |
22 | | #include "ntp_control.h" |
23 | | #include "ntp_unixtime.h" |
24 | | #include "ntp_stdlib.h" |
25 | | #include "ntp_config.h" |
26 | | #include "ntp_crypto.h" |
27 | | #include "ntp_assert.h" |
28 | | #include "ntp_leapsec.h" |
29 | | #include "ntp_md5.h" /* provides OpenSSL digest API */ |
30 | | #include "lib_strbuf.h" |
31 | | #include <rc_cmdlength.h> |
32 | | #ifdef KERNEL_PLL |
33 | | # include "ntp_syscall.h" |
34 | | #endif |
35 | | |
36 | | /* |
37 | | * Structure to hold request procedure information |
38 | | */ |
39 | | |
40 | | struct ctl_proc { |
41 | | short control_code; /* defined request code */ |
42 | 10.1k | #define NO_REQUEST (-1) |
43 | | u_short flags; /* flags word */ |
44 | | /* Only one flag. Authentication required or not. */ |
45 | | #define NOAUTH 0 |
46 | 2.43k | #define AUTH 1 |
47 | | void (*handler) (struct recvbuf *, int); /* handle request */ |
48 | | }; |
49 | | |
50 | | |
51 | | /* |
52 | | * Request processing routines |
53 | | */ |
54 | | static void ctl_error (u_char); |
55 | | #ifdef REFCLOCK |
56 | | static u_short ctlclkstatus (struct refclockstat *); |
57 | | #endif |
58 | | static void ctl_flushpkt (u_char); |
59 | | static void ctl_putdata (const char *, unsigned int, int); |
60 | | static void ctl_putstr (const char *, const char *, size_t); |
61 | | static void ctl_putdblf (const char *, int, int, double); |
62 | 183 | #define ctl_putdbl(tag, d) ctl_putdblf(tag, 1, 3, d) |
63 | 70 | #define ctl_putdbl6(tag, d) ctl_putdblf(tag, 1, 6, d) |
64 | 0 | #define ctl_putsfp(tag, sfp) ctl_putdblf(tag, 0, -1, \ |
65 | 0 | FPTOD(sfp)) |
66 | | static void ctl_putuint (const char *, u_long); |
67 | | static void ctl_puthex (const char *, u_long); |
68 | | static void ctl_putint (const char *, long); |
69 | | static void ctl_putts (const char *, l_fp *); |
70 | | static void ctl_putadr (const char *, u_int32, |
71 | | sockaddr_u *); |
72 | | static void ctl_putrefid (const char *, u_int32); |
73 | | static void ctl_putarray (const char *, double *, int); |
74 | | static void ctl_putsys (int); |
75 | | static void ctl_putpeer (int, struct peer *); |
76 | | static void ctl_putfs (const char *, tstamp_t); |
77 | | static void ctl_printf (const char *, ...) NTP_PRINTF(1, 2); |
78 | | #ifdef REFCLOCK |
79 | | static void ctl_putclock (int, struct refclockstat *, int); |
80 | | #endif /* REFCLOCK */ |
81 | | static const struct ctl_var *ctl_getitem(const struct ctl_var *, |
82 | | char **); |
83 | | static u_short count_var (const struct ctl_var *); |
84 | | static void control_unspec (struct recvbuf *, int); |
85 | | static void read_status (struct recvbuf *, int); |
86 | | static void read_sysvars (void); |
87 | | static void read_peervars (void); |
88 | | static void read_variables (struct recvbuf *, int); |
89 | | static void write_variables (struct recvbuf *, int); |
90 | | static void read_clockstatus(struct recvbuf *, int); |
91 | | static void write_clockstatus(struct recvbuf *, int); |
92 | | static void set_trap (struct recvbuf *, int); |
93 | | static void save_config (struct recvbuf *, int); |
94 | | static void configure (struct recvbuf *, int); |
95 | | static void send_mru_entry (mon_entry *, int); |
96 | | static void send_random_tag_value(int); |
97 | | static void read_mru_list (struct recvbuf *, int); |
98 | | static void send_ifstats_entry(endpt *, u_int); |
99 | | static void read_ifstats (struct recvbuf *); |
100 | | static void sockaddrs_from_restrict_u(sockaddr_u *, sockaddr_u *, |
101 | | restrict_u *, int); |
102 | | static void send_restrict_entry(restrict_u *, int, u_int); |
103 | | static void send_restrict_list(restrict_u *, int, u_int *); |
104 | | static void read_addr_restrictions(struct recvbuf *); |
105 | | static void read_ordlist (struct recvbuf *, int); |
106 | | static u_int32 derive_nonce (sockaddr_u *, u_int32, u_int32); |
107 | | static void generate_nonce (struct recvbuf *, char *, size_t); |
108 | | static int validate_nonce (const char *, struct recvbuf *); |
109 | | static void req_nonce (struct recvbuf *, int); |
110 | | static void unset_trap (struct recvbuf *, int); |
111 | | static struct ctl_trap *ctlfindtrap(sockaddr_u *, |
112 | | struct interface *); |
113 | | |
114 | | int/*BOOL*/ is_safe_filename(const char * name); |
115 | | |
116 | | static const struct ctl_proc control_codes[] = { |
117 | | { CTL_OP_UNSPEC, NOAUTH, control_unspec }, |
118 | | { CTL_OP_READSTAT, NOAUTH, read_status }, |
119 | | { CTL_OP_READVAR, NOAUTH, read_variables }, |
120 | | { CTL_OP_WRITEVAR, AUTH, write_variables }, |
121 | | { CTL_OP_READCLOCK, NOAUTH, read_clockstatus }, |
122 | | { CTL_OP_WRITECLOCK, AUTH, write_clockstatus }, |
123 | | { CTL_OP_SETTRAP, AUTH, set_trap }, |
124 | | { CTL_OP_CONFIGURE, AUTH, configure }, |
125 | | { CTL_OP_SAVECONFIG, AUTH, save_config }, |
126 | | { CTL_OP_READ_MRU, NOAUTH, read_mru_list }, |
127 | | { CTL_OP_READ_ORDLIST_A, AUTH, read_ordlist }, |
128 | | { CTL_OP_REQ_NONCE, NOAUTH, req_nonce }, |
129 | | { CTL_OP_UNSETTRAP, AUTH, unset_trap }, |
130 | | { NO_REQUEST, 0, NULL } |
131 | | }; |
132 | | |
133 | | /* |
134 | | * System variables we understand |
135 | | */ |
136 | 72 | #define CS_LEAP 1 |
137 | 70 | #define CS_STRATUM 2 |
138 | 70 | #define CS_PRECISION 3 |
139 | 35 | #define CS_ROOTDELAY 4 |
140 | 35 | #define CS_ROOTDISPERSION 5 |
141 | 35 | #define CS_REFID 6 |
142 | 70 | #define CS_REFTIME 7 |
143 | 96 | #define CS_POLL 8 |
144 | 84 | #define CS_PEERID 9 |
145 | 35 | #define CS_OFFSET 10 |
146 | 35 | #define CS_DRIFT 11 |
147 | 35 | #define CS_JITTER 12 |
148 | 35 | #define CS_ERROR 13 |
149 | 86 | #define CS_CLOCK 14 |
150 | 70 | #define CS_PROCESSOR 15 |
151 | 84 | #define CS_SYSTEM 16 |
152 | 70 | #define CS_VERSION 17 |
153 | 35 | #define CS_STABIL 18 |
154 | 0 | #define CS_VARLIST 19 |
155 | 36 | #define CS_TAI 20 |
156 | 35 | #define CS_LEAPTAB 21 |
157 | 35 | #define CS_LEAPEND 22 |
158 | 76 | #define CS_RATE 23 |
159 | 0 | #define CS_MRU_ENABLED 24 |
160 | 0 | #define CS_MRU_DEPTH 25 |
161 | 0 | #define CS_MRU_DEEPEST 26 |
162 | 0 | #define CS_MRU_MINDEPTH 27 |
163 | 0 | #define CS_MRU_MAXAGE 28 |
164 | 0 | #define CS_MRU_MAXDEPTH 29 |
165 | 0 | #define CS_MRU_MEM 30 |
166 | 0 | #define CS_MRU_MAXMEM 31 |
167 | 0 | #define CS_SS_UPTIME 32 |
168 | 0 | #define CS_SS_RESET 33 |
169 | 0 | #define CS_SS_RECEIVED 34 |
170 | 0 | #define CS_SS_THISVER 35 |
171 | 0 | #define CS_SS_OLDVER 36 |
172 | 0 | #define CS_SS_BADFORMAT 37 |
173 | 0 | #define CS_SS_BADAUTH 38 |
174 | 0 | #define CS_SS_DECLINED 39 |
175 | 0 | #define CS_SS_RESTRICTED 40 |
176 | 0 | #define CS_SS_LIMITED 41 |
177 | 0 | #define CS_SS_KODSENT 42 |
178 | 0 | #define CS_SS_PROCESSED 43 |
179 | 0 | #define CS_SS_LAMPORT 44 |
180 | 0 | #define CS_SS_TSROUNDING 45 |
181 | 8 | #define CS_PEERADR 46 |
182 | 0 | #define CS_PEERMODE 47 |
183 | 0 | #define CS_BCASTDELAY 48 |
184 | 0 | #define CS_AUTHDELAY 49 |
185 | 0 | #define CS_AUTHKEYS 50 |
186 | 0 | #define CS_AUTHFREEK 51 |
187 | 0 | #define CS_AUTHKLOOKUPS 52 |
188 | 0 | #define CS_AUTHKNOTFOUND 53 |
189 | 0 | #define CS_AUTHKUNCACHED 54 |
190 | 0 | #define CS_AUTHKEXPIRED 55 |
191 | 0 | #define CS_AUTHENCRYPTS 56 |
192 | 0 | #define CS_AUTHDECRYPTS 57 |
193 | 0 | #define CS_AUTHRESET 58 |
194 | 892 | #define CS_K_OFFSET 59 |
195 | 0 | #define CS_K_FREQ 60 |
196 | 0 | #define CS_K_MAXERR 61 |
197 | 0 | #define CS_K_ESTERR 62 |
198 | 0 | #define CS_K_STFLAGS 63 |
199 | 0 | #define CS_K_TIMECONST 64 |
200 | 0 | #define CS_K_PRECISION 65 |
201 | 0 | #define CS_K_FREQTOL 66 |
202 | 0 | #define CS_K_PPS_FREQ 67 |
203 | 0 | #define CS_K_PPS_STABIL 68 |
204 | 0 | #define CS_K_PPS_JITTER 69 |
205 | 0 | #define CS_K_PPS_CALIBDUR 70 |
206 | 0 | #define CS_K_PPS_CALIBS 71 |
207 | 0 | #define CS_K_PPS_CALIBERRS 72 |
208 | 0 | #define CS_K_PPS_JITEXC 73 |
209 | 970 | #define CS_K_PPS_STBEXC 74 |
210 | 892 | #define CS_KERN_FIRST CS_K_OFFSET |
211 | 78 | #define CS_KERN_LAST CS_K_PPS_STBEXC |
212 | 0 | #define CS_IOSTATS_RESET 75 |
213 | 0 | #define CS_TOTAL_RBUF 76 |
214 | 0 | #define CS_FREE_RBUF 77 |
215 | 0 | #define CS_USED_RBUF 78 |
216 | 0 | #define CS_RBUF_LOWATER 79 |
217 | 0 | #define CS_IO_DROPPED 80 |
218 | 0 | #define CS_IO_IGNORED 81 |
219 | 0 | #define CS_IO_RECEIVED 82 |
220 | 0 | #define CS_IO_SENT 83 |
221 | 0 | #define CS_IO_SENDFAILED 84 |
222 | 0 | #define CS_IO_WAKEUPS 85 |
223 | 0 | #define CS_IO_GOODWAKEUPS 86 |
224 | 0 | #define CS_TIMERSTATS_RESET 87 |
225 | 0 | #define CS_TIMER_OVERRUNS 88 |
226 | 0 | #define CS_TIMER_XMTS 89 |
227 | 8 | #define CS_FUZZ 90 |
228 | 0 | #define CS_WANDER_THRESH 91 |
229 | | #define CS_LEAPSMEARINTV 92 |
230 | 2.31k | #define CS_LEAPSMEAROFFS 93 |
231 | 2.31k | #define CS_MAX_NOAUTOKEY CS_LEAPSMEAROFFS |
232 | | #ifdef AUTOKEY |
233 | | #define CS_FLAGS (1 + CS_MAX_NOAUTOKEY) |
234 | | #define CS_HOST (2 + CS_MAX_NOAUTOKEY) |
235 | | #define CS_PUBLIC (3 + CS_MAX_NOAUTOKEY) |
236 | | #define CS_CERTIF (4 + CS_MAX_NOAUTOKEY) |
237 | | #define CS_SIGNATURE (5 + CS_MAX_NOAUTOKEY) |
238 | | #define CS_REVTIME (6 + CS_MAX_NOAUTOKEY) |
239 | | #define CS_IDENT (7 + CS_MAX_NOAUTOKEY) |
240 | | #define CS_DIGEST (8 + CS_MAX_NOAUTOKEY) |
241 | | #define CS_MAXCODE CS_DIGEST |
242 | | #else /* !AUTOKEY follows */ |
243 | 2.31k | #define CS_MAXCODE CS_MAX_NOAUTOKEY |
244 | | #endif /* !AUTOKEY */ |
245 | | |
246 | | /* |
247 | | * Peer variables we understand |
248 | | */ |
249 | 0 | #define CP_CONFIG 1 |
250 | 0 | #define CP_AUTHENABLE 2 |
251 | 0 | #define CP_AUTHENTIC 3 |
252 | 0 | #define CP_SRCADR 4 |
253 | 0 | #define CP_SRCPORT 5 |
254 | 0 | #define CP_DSTADR 6 |
255 | 0 | #define CP_DSTPORT 7 |
256 | 0 | #define CP_LEAP 8 |
257 | 0 | #define CP_HMODE 9 |
258 | 0 | #define CP_STRATUM 10 |
259 | 0 | #define CP_PPOLL 11 |
260 | 0 | #define CP_HPOLL 12 |
261 | 0 | #define CP_PRECISION 13 |
262 | 0 | #define CP_ROOTDELAY 14 |
263 | 0 | #define CP_ROOTDISPERSION 15 |
264 | 0 | #define CP_REFID 16 |
265 | 0 | #define CP_REFTIME 17 |
266 | 0 | #define CP_ORG 18 |
267 | 0 | #define CP_REC 19 |
268 | 0 | #define CP_XMT 20 |
269 | 0 | #define CP_REACH 21 |
270 | 0 | #define CP_UNREACH 22 |
271 | 0 | #define CP_TIMER 23 |
272 | 0 | #define CP_DELAY 24 |
273 | 0 | #define CP_OFFSET 25 |
274 | 0 | #define CP_JITTER 26 |
275 | 0 | #define CP_DISPERSION 27 |
276 | 0 | #define CP_KEYID 28 |
277 | 0 | #define CP_FILTDELAY 29 |
278 | 0 | #define CP_FILTOFFSET 30 |
279 | 0 | #define CP_PMODE 31 |
280 | 0 | #define CP_RECEIVED 32 |
281 | 0 | #define CP_SENT 33 |
282 | 0 | #define CP_FILTERROR 34 |
283 | 0 | #define CP_FLASH 35 |
284 | 0 | #define CP_TTL 36 |
285 | 0 | #define CP_VARLIST 37 |
286 | 0 | #define CP_IN 38 |
287 | 0 | #define CP_OUT 39 |
288 | 0 | #define CP_RATE 40 |
289 | 0 | #define CP_BIAS 41 |
290 | 0 | #define CP_SRCHOST 42 |
291 | 0 | #define CP_TIMEREC 43 |
292 | 0 | #define CP_TIMEREACH 44 |
293 | 0 | #define CP_BADAUTH 45 |
294 | 0 | #define CP_BOGUSORG 46 |
295 | 0 | #define CP_OLDPKT 47 |
296 | 0 | #define CP_SELDISP 48 |
297 | 0 | #define CP_SELBROKEN 49 |
298 | 0 | #define CP_CANDIDATE 50 |
299 | 0 | #define CP_MAX_NOAUTOKEY CP_CANDIDATE |
300 | | #ifdef AUTOKEY |
301 | | #define CP_FLAGS (1 + CP_MAX_NOAUTOKEY) |
302 | | #define CP_HOST (2 + CP_MAX_NOAUTOKEY) |
303 | | #define CP_VALID (3 + CP_MAX_NOAUTOKEY) |
304 | | #define CP_INITSEQ (4 + CP_MAX_NOAUTOKEY) |
305 | | #define CP_INITKEY (5 + CP_MAX_NOAUTOKEY) |
306 | | #define CP_INITTSP (6 + CP_MAX_NOAUTOKEY) |
307 | | #define CP_SIGNATURE (7 + CP_MAX_NOAUTOKEY) |
308 | | #define CP_IDENT (8 + CP_MAX_NOAUTOKEY) |
309 | | #define CP_MAXCODE CP_IDENT |
310 | | #else /* !AUTOKEY follows */ |
311 | | #define CP_MAXCODE CP_MAX_NOAUTOKEY |
312 | | #endif /* !AUTOKEY */ |
313 | | |
314 | | /* |
315 | | * Clock variables we understand |
316 | | */ |
317 | 0 | #define CC_TYPE 1 |
318 | 0 | #define CC_TIMECODE 2 |
319 | 0 | #define CC_POLL 3 |
320 | 0 | #define CC_NOREPLY 4 |
321 | 0 | #define CC_BADFORMAT 5 |
322 | 0 | #define CC_BADDATA 6 |
323 | 0 | #define CC_FUDGETIME1 7 |
324 | 0 | #define CC_FUDGETIME2 8 |
325 | 0 | #define CC_FUDGEVAL1 9 |
326 | 0 | #define CC_FUDGEVAL2 10 |
327 | 0 | #define CC_FLAGS 11 |
328 | 0 | #define CC_DEVICE 12 |
329 | 0 | #define CC_VARLIST 13 |
330 | 0 | #define CC_MAXCODE CC_VARLIST |
331 | | |
332 | | /* |
333 | | * System variable values. The array can be indexed by the variable |
334 | | * index to find the textual name. |
335 | | */ |
336 | | static const struct ctl_var sys_var[] = { |
337 | | { 0, PADDING, "" }, /* 0 */ |
338 | | { CS_LEAP, RW, "leap" }, /* 1 */ |
339 | | { CS_STRATUM, RO, "stratum" }, /* 2 */ |
340 | | { CS_PRECISION, RO, "precision" }, /* 3 */ |
341 | | { CS_ROOTDELAY, RO, "rootdelay" }, /* 4 */ |
342 | | { CS_ROOTDISPERSION, RO, "rootdisp" }, /* 5 */ |
343 | | { CS_REFID, RO, "refid" }, /* 6 */ |
344 | | { CS_REFTIME, RO, "reftime" }, /* 7 */ |
345 | | { CS_POLL, RO, "tc" }, /* 8 */ |
346 | | { CS_PEERID, RO, "peer" }, /* 9 */ |
347 | | { CS_OFFSET, RO, "offset" }, /* 10 */ |
348 | | { CS_DRIFT, RO, "frequency" }, /* 11 */ |
349 | | { CS_JITTER, RO, "sys_jitter" }, /* 12 */ |
350 | | { CS_ERROR, RO, "clk_jitter" }, /* 13 */ |
351 | | { CS_CLOCK, RO, "clock" }, /* 14 */ |
352 | | { CS_PROCESSOR, RO, "processor" }, /* 15 */ |
353 | | { CS_SYSTEM, RO, "system" }, /* 16 */ |
354 | | { CS_VERSION, RO, "version" }, /* 17 */ |
355 | | { CS_STABIL, RO, "clk_wander" }, /* 18 */ |
356 | | { CS_VARLIST, RO, "sys_var_list" }, /* 19 */ |
357 | | { CS_TAI, RO, "tai" }, /* 20 */ |
358 | | { CS_LEAPTAB, RO, "leapsec" }, /* 21 */ |
359 | | { CS_LEAPEND, RO, "expire" }, /* 22 */ |
360 | | { CS_RATE, RO, "mintc" }, /* 23 */ |
361 | | { CS_MRU_ENABLED, RO, "mru_enabled" }, /* 24 */ |
362 | | { CS_MRU_DEPTH, RO, "mru_depth" }, /* 25 */ |
363 | | { CS_MRU_DEEPEST, RO, "mru_deepest" }, /* 26 */ |
364 | | { CS_MRU_MINDEPTH, RO, "mru_mindepth" }, /* 27 */ |
365 | | { CS_MRU_MAXAGE, RO, "mru_maxage" }, /* 28 */ |
366 | | { CS_MRU_MAXDEPTH, RO, "mru_maxdepth" }, /* 29 */ |
367 | | { CS_MRU_MEM, RO, "mru_mem" }, /* 30 */ |
368 | | { CS_MRU_MAXMEM, RO, "mru_maxmem" }, /* 31 */ |
369 | | { CS_SS_UPTIME, RO, "ss_uptime" }, /* 32 */ |
370 | | { CS_SS_RESET, RO, "ss_reset" }, /* 33 */ |
371 | | { CS_SS_RECEIVED, RO, "ss_received" }, /* 34 */ |
372 | | { CS_SS_THISVER, RO, "ss_thisver" }, /* 35 */ |
373 | | { CS_SS_OLDVER, RO, "ss_oldver" }, /* 36 */ |
374 | | { CS_SS_BADFORMAT, RO, "ss_badformat" }, /* 37 */ |
375 | | { CS_SS_BADAUTH, RO, "ss_badauth" }, /* 38 */ |
376 | | { CS_SS_DECLINED, RO, "ss_declined" }, /* 39 */ |
377 | | { CS_SS_RESTRICTED, RO, "ss_restricted" }, /* 40 */ |
378 | | { CS_SS_LIMITED, RO, "ss_limited" }, /* 41 */ |
379 | | { CS_SS_KODSENT, RO, "ss_kodsent" }, /* 42 */ |
380 | | { CS_SS_PROCESSED, RO, "ss_processed" }, /* 43 */ |
381 | | { CS_SS_LAMPORT, RO, "ss_lamport" }, /* 44 */ |
382 | | { CS_SS_TSROUNDING, RO, "ss_tsrounding" }, /* 45 */ |
383 | | { CS_PEERADR, RO, "peeradr" }, /* 46 */ |
384 | | { CS_PEERMODE, RO, "peermode" }, /* 47 */ |
385 | | { CS_BCASTDELAY, RO, "bcastdelay" }, /* 48 */ |
386 | | { CS_AUTHDELAY, RO, "authdelay" }, /* 49 */ |
387 | | { CS_AUTHKEYS, RO, "authkeys" }, /* 50 */ |
388 | | { CS_AUTHFREEK, RO, "authfreek" }, /* 51 */ |
389 | | { CS_AUTHKLOOKUPS, RO, "authklookups" }, /* 52 */ |
390 | | { CS_AUTHKNOTFOUND, RO, "authknotfound" }, /* 53 */ |
391 | | { CS_AUTHKUNCACHED, RO, "authkuncached" }, /* 54 */ |
392 | | { CS_AUTHKEXPIRED, RO, "authkexpired" }, /* 55 */ |
393 | | { CS_AUTHENCRYPTS, RO, "authencrypts" }, /* 56 */ |
394 | | { CS_AUTHDECRYPTS, RO, "authdecrypts" }, /* 57 */ |
395 | | { CS_AUTHRESET, RO, "authreset" }, /* 58 */ |
396 | | { CS_K_OFFSET, RO, "koffset" }, /* 59 */ |
397 | | { CS_K_FREQ, RO, "kfreq" }, /* 60 */ |
398 | | { CS_K_MAXERR, RO, "kmaxerr" }, /* 61 */ |
399 | | { CS_K_ESTERR, RO, "kesterr" }, /* 62 */ |
400 | | { CS_K_STFLAGS, RO, "kstflags" }, /* 63 */ |
401 | | { CS_K_TIMECONST, RO, "ktimeconst" }, /* 64 */ |
402 | | { CS_K_PRECISION, RO, "kprecis" }, /* 65 */ |
403 | | { CS_K_FREQTOL, RO, "kfreqtol" }, /* 66 */ |
404 | | { CS_K_PPS_FREQ, RO, "kppsfreq" }, /* 67 */ |
405 | | { CS_K_PPS_STABIL, RO, "kppsstab" }, /* 68 */ |
406 | | { CS_K_PPS_JITTER, RO, "kppsjitter" }, /* 69 */ |
407 | | { CS_K_PPS_CALIBDUR, RO, "kppscalibdur" }, /* 70 */ |
408 | | { CS_K_PPS_CALIBS, RO, "kppscalibs" }, /* 71 */ |
409 | | { CS_K_PPS_CALIBERRS, RO, "kppscaliberrs" }, /* 72 */ |
410 | | { CS_K_PPS_JITEXC, RO, "kppsjitexc" }, /* 73 */ |
411 | | { CS_K_PPS_STBEXC, RO, "kppsstbexc" }, /* 74 */ |
412 | | { CS_IOSTATS_RESET, RO, "iostats_reset" }, /* 75 */ |
413 | | { CS_TOTAL_RBUF, RO, "total_rbuf" }, /* 76 */ |
414 | | { CS_FREE_RBUF, RO, "free_rbuf" }, /* 77 */ |
415 | | { CS_USED_RBUF, RO, "used_rbuf" }, /* 78 */ |
416 | | { CS_RBUF_LOWATER, RO, "rbuf_lowater" }, /* 79 */ |
417 | | { CS_IO_DROPPED, RO, "io_dropped" }, /* 80 */ |
418 | | { CS_IO_IGNORED, RO, "io_ignored" }, /* 81 */ |
419 | | { CS_IO_RECEIVED, RO, "io_received" }, /* 82 */ |
420 | | { CS_IO_SENT, RO, "io_sent" }, /* 83 */ |
421 | | { CS_IO_SENDFAILED, RO, "io_sendfailed" }, /* 84 */ |
422 | | { CS_IO_WAKEUPS, RO, "io_wakeups" }, /* 85 */ |
423 | | { CS_IO_GOODWAKEUPS, RO, "io_goodwakeups" }, /* 86 */ |
424 | | { CS_TIMERSTATS_RESET, RO, "timerstats_reset" },/* 87 */ |
425 | | { CS_TIMER_OVERRUNS, RO, "timer_overruns" }, /* 88 */ |
426 | | { CS_TIMER_XMTS, RO, "timer_xmts" }, /* 89 */ |
427 | | { CS_FUZZ, RO, "fuzz" }, /* 90 */ |
428 | | { CS_WANDER_THRESH, RO, "clk_wander_threshold" }, /* 91 */ |
429 | | |
430 | | { CS_LEAPSMEARINTV, RO, "leapsmearinterval" }, /* 92 */ |
431 | | { CS_LEAPSMEAROFFS, RO, "leapsmearoffset" }, /* 93 */ |
432 | | |
433 | | #ifdef AUTOKEY |
434 | | { CS_FLAGS, RO, "flags" }, /* 1 + CS_MAX_NOAUTOKEY */ |
435 | | { CS_HOST, RO, "host" }, /* 2 + CS_MAX_NOAUTOKEY */ |
436 | | { CS_PUBLIC, RO, "update" }, /* 3 + CS_MAX_NOAUTOKEY */ |
437 | | { CS_CERTIF, RO, "cert" }, /* 4 + CS_MAX_NOAUTOKEY */ |
438 | | { CS_SIGNATURE, RO, "signature" }, /* 5 + CS_MAX_NOAUTOKEY */ |
439 | | { CS_REVTIME, RO, "until" }, /* 6 + CS_MAX_NOAUTOKEY */ |
440 | | { CS_IDENT, RO, "ident" }, /* 7 + CS_MAX_NOAUTOKEY */ |
441 | | { CS_DIGEST, RO, "digest" }, /* 8 + CS_MAX_NOAUTOKEY */ |
442 | | #endif /* AUTOKEY */ |
443 | | { 0, EOV, "" } /* 94/102 */ |
444 | | }; |
445 | | |
446 | | static struct ctl_var *ext_sys_var = NULL; |
447 | | |
448 | | /* |
449 | | * System variables we print by default (in fuzzball order, |
450 | | * more-or-less) |
451 | | */ |
452 | | static const u_char def_sys_var[] = { |
453 | | CS_VERSION, |
454 | | CS_PROCESSOR, |
455 | | CS_SYSTEM, |
456 | | CS_LEAP, |
457 | | CS_STRATUM, |
458 | | CS_PRECISION, |
459 | | CS_ROOTDELAY, |
460 | | CS_ROOTDISPERSION, |
461 | | CS_REFID, |
462 | | CS_REFTIME, |
463 | | CS_CLOCK, |
464 | | CS_PEERID, |
465 | | CS_POLL, |
466 | | CS_RATE, |
467 | | CS_OFFSET, |
468 | | CS_DRIFT, |
469 | | CS_JITTER, |
470 | | CS_ERROR, |
471 | | CS_STABIL, |
472 | | CS_TAI, |
473 | | CS_LEAPTAB, |
474 | | CS_LEAPEND, |
475 | | CS_LEAPSMEARINTV, |
476 | | CS_LEAPSMEAROFFS, |
477 | | #ifdef AUTOKEY |
478 | | CS_HOST, |
479 | | CS_IDENT, |
480 | | CS_FLAGS, |
481 | | CS_DIGEST, |
482 | | CS_SIGNATURE, |
483 | | CS_PUBLIC, |
484 | | CS_CERTIF, |
485 | | #endif /* AUTOKEY */ |
486 | | 0 |
487 | | }; |
488 | | |
489 | | |
490 | | /* |
491 | | * Peer variable list |
492 | | */ |
493 | | static const struct ctl_var peer_var[] = { |
494 | | { 0, PADDING, "" }, /* 0 */ |
495 | | { CP_CONFIG, RO, "config" }, /* 1 */ |
496 | | { CP_AUTHENABLE, RO, "authenable" }, /* 2 */ |
497 | | { CP_AUTHENTIC, RO, "authentic" }, /* 3 */ |
498 | | { CP_SRCADR, RO, "srcadr" }, /* 4 */ |
499 | | { CP_SRCPORT, RO, "srcport" }, /* 5 */ |
500 | | { CP_DSTADR, RO, "dstadr" }, /* 6 */ |
501 | | { CP_DSTPORT, RO, "dstport" }, /* 7 */ |
502 | | { CP_LEAP, RO, "leap" }, /* 8 */ |
503 | | { CP_HMODE, RO, "hmode" }, /* 9 */ |
504 | | { CP_STRATUM, RO, "stratum" }, /* 10 */ |
505 | | { CP_PPOLL, RO, "ppoll" }, /* 11 */ |
506 | | { CP_HPOLL, RO, "hpoll" }, /* 12 */ |
507 | | { CP_PRECISION, RO, "precision" }, /* 13 */ |
508 | | { CP_ROOTDELAY, RO, "rootdelay" }, /* 14 */ |
509 | | { CP_ROOTDISPERSION, RO, "rootdisp" }, /* 15 */ |
510 | | { CP_REFID, RO, "refid" }, /* 16 */ |
511 | | { CP_REFTIME, RO, "reftime" }, /* 17 */ |
512 | | { CP_ORG, RO, "org" }, /* 18 */ |
513 | | { CP_REC, RO, "rec" }, /* 19 */ |
514 | | { CP_XMT, RO, "xleave" }, /* 20 */ |
515 | | { CP_REACH, RO, "reach" }, /* 21 */ |
516 | | { CP_UNREACH, RO, "unreach" }, /* 22 */ |
517 | | { CP_TIMER, RO, "timer" }, /* 23 */ |
518 | | { CP_DELAY, RO, "delay" }, /* 24 */ |
519 | | { CP_OFFSET, RO, "offset" }, /* 25 */ |
520 | | { CP_JITTER, RO, "jitter" }, /* 26 */ |
521 | | { CP_DISPERSION, RO, "dispersion" }, /* 27 */ |
522 | | { CP_KEYID, RO, "keyid" }, /* 28 */ |
523 | | { CP_FILTDELAY, RO, "filtdelay" }, /* 29 */ |
524 | | { CP_FILTOFFSET, RO, "filtoffset" }, /* 30 */ |
525 | | { CP_PMODE, RO, "pmode" }, /* 31 */ |
526 | | { CP_RECEIVED, RO, "received"}, /* 32 */ |
527 | | { CP_SENT, RO, "sent" }, /* 33 */ |
528 | | { CP_FILTERROR, RO, "filtdisp" }, /* 34 */ |
529 | | { CP_FLASH, RO, "flash" }, /* 35 */ |
530 | | { CP_TTL, RO, "ttl" }, /* 36 */ |
531 | | { CP_VARLIST, RO, "peer_var_list" }, /* 37 */ |
532 | | { CP_IN, RO, "in" }, /* 38 */ |
533 | | { CP_OUT, RO, "out" }, /* 39 */ |
534 | | { CP_RATE, RO, "headway" }, /* 40 */ |
535 | | { CP_BIAS, RO, "bias" }, /* 41 */ |
536 | | { CP_SRCHOST, RO, "srchost" }, /* 42 */ |
537 | | { CP_TIMEREC, RO, "timerec" }, /* 43 */ |
538 | | { CP_TIMEREACH, RO, "timereach" }, /* 44 */ |
539 | | { CP_BADAUTH, RO, "badauth" }, /* 45 */ |
540 | | { CP_BOGUSORG, RO, "bogusorg" }, /* 46 */ |
541 | | { CP_OLDPKT, RO, "oldpkt" }, /* 47 */ |
542 | | { CP_SELDISP, RO, "seldisp" }, /* 48 */ |
543 | | { CP_SELBROKEN, RO, "selbroken" }, /* 49 */ |
544 | | { CP_CANDIDATE, RO, "candidate" }, /* 50 */ |
545 | | #ifdef AUTOKEY |
546 | | { CP_FLAGS, RO, "flags" }, /* 1 + CP_MAX_NOAUTOKEY */ |
547 | | { CP_HOST, RO, "host" }, /* 2 + CP_MAX_NOAUTOKEY */ |
548 | | { CP_VALID, RO, "valid" }, /* 3 + CP_MAX_NOAUTOKEY */ |
549 | | { CP_INITSEQ, RO, "initsequence" }, /* 4 + CP_MAX_NOAUTOKEY */ |
550 | | { CP_INITKEY, RO, "initkey" }, /* 5 + CP_MAX_NOAUTOKEY */ |
551 | | { CP_INITTSP, RO, "timestamp" }, /* 6 + CP_MAX_NOAUTOKEY */ |
552 | | { CP_SIGNATURE, RO, "signature" }, /* 7 + CP_MAX_NOAUTOKEY */ |
553 | | { CP_IDENT, RO, "ident" }, /* 8 + CP_MAX_NOAUTOKEY */ |
554 | | #endif /* AUTOKEY */ |
555 | | { 0, EOV, "" } /* 50/58 */ |
556 | | }; |
557 | | |
558 | | |
559 | | /* |
560 | | * Peer variables we print by default |
561 | | */ |
562 | | static const u_char def_peer_var[] = { |
563 | | CP_SRCADR, |
564 | | CP_SRCPORT, |
565 | | CP_SRCHOST, |
566 | | CP_DSTADR, |
567 | | CP_DSTPORT, |
568 | | CP_OUT, |
569 | | CP_IN, |
570 | | CP_LEAP, |
571 | | CP_STRATUM, |
572 | | CP_PRECISION, |
573 | | CP_ROOTDELAY, |
574 | | CP_ROOTDISPERSION, |
575 | | CP_REFID, |
576 | | CP_REFTIME, |
577 | | CP_REC, |
578 | | CP_REACH, |
579 | | CP_UNREACH, |
580 | | CP_HMODE, |
581 | | CP_PMODE, |
582 | | CP_HPOLL, |
583 | | CP_PPOLL, |
584 | | CP_RATE, |
585 | | CP_FLASH, |
586 | | CP_KEYID, |
587 | | CP_TTL, |
588 | | CP_OFFSET, |
589 | | CP_DELAY, |
590 | | CP_DISPERSION, |
591 | | CP_JITTER, |
592 | | CP_XMT, |
593 | | CP_BIAS, |
594 | | CP_FILTDELAY, |
595 | | CP_FILTOFFSET, |
596 | | CP_FILTERROR, |
597 | | #ifdef AUTOKEY |
598 | | CP_HOST, |
599 | | CP_FLAGS, |
600 | | CP_SIGNATURE, |
601 | | CP_VALID, |
602 | | CP_INITSEQ, |
603 | | CP_IDENT, |
604 | | #endif /* AUTOKEY */ |
605 | | 0 |
606 | | }; |
607 | | |
608 | | |
609 | | #ifdef REFCLOCK |
610 | | /* |
611 | | * Clock variable list |
612 | | */ |
613 | | static const struct ctl_var clock_var[] = { |
614 | | { 0, PADDING, "" }, /* 0 */ |
615 | | { CC_TYPE, RO, "type" }, /* 1 */ |
616 | | { CC_TIMECODE, RO, "timecode" }, /* 2 */ |
617 | | { CC_POLL, RO, "poll" }, /* 3 */ |
618 | | { CC_NOREPLY, RO, "noreply" }, /* 4 */ |
619 | | { CC_BADFORMAT, RO, "badformat" }, /* 5 */ |
620 | | { CC_BADDATA, RO, "baddata" }, /* 6 */ |
621 | | { CC_FUDGETIME1, RO, "fudgetime1" }, /* 7 */ |
622 | | { CC_FUDGETIME2, RO, "fudgetime2" }, /* 8 */ |
623 | | { CC_FUDGEVAL1, RO, "stratum" }, /* 9 */ |
624 | | { CC_FUDGEVAL2, RO, "refid" }, /* 10 */ |
625 | | { CC_FLAGS, RO, "flags" }, /* 11 */ |
626 | | { CC_DEVICE, RO, "device" }, /* 12 */ |
627 | | { CC_VARLIST, RO, "clock_var_list" }, /* 13 */ |
628 | | { 0, EOV, "" } /* 14 */ |
629 | | }; |
630 | | |
631 | | |
632 | | /* |
633 | | * Clock variables printed by default |
634 | | */ |
635 | | static const u_char def_clock_var[] = { |
636 | | CC_DEVICE, |
637 | | CC_TYPE, /* won't be output if device = known */ |
638 | | CC_TIMECODE, |
639 | | CC_POLL, |
640 | | CC_NOREPLY, |
641 | | CC_BADFORMAT, |
642 | | CC_BADDATA, |
643 | | CC_FUDGETIME1, |
644 | | CC_FUDGETIME2, |
645 | | CC_FUDGEVAL1, |
646 | | CC_FUDGEVAL2, |
647 | | CC_FLAGS, |
648 | | 0 |
649 | | }; |
650 | | #endif |
651 | | |
652 | | /* |
653 | | * MRU string constants shared by send_mru_entry() and read_mru_list(). |
654 | | */ |
655 | | static const char addr_fmt[] = "addr.%d"; |
656 | | static const char last_fmt[] = "last.%d"; |
657 | | |
658 | | /* |
659 | | * System and processor definitions. |
660 | | */ |
661 | | #ifndef HAVE_UNAME |
662 | | # ifndef STR_SYSTEM |
663 | | # define STR_SYSTEM "UNIX" |
664 | | # endif |
665 | | # ifndef STR_PROCESSOR |
666 | | # define STR_PROCESSOR "unknown" |
667 | | # endif |
668 | | |
669 | | static const char str_system[] = STR_SYSTEM; |
670 | | static const char str_processor[] = STR_PROCESSOR; |
671 | | #else |
672 | | # include <sys/utsname.h> |
673 | | static struct utsname utsnamebuf; |
674 | | #endif /* HAVE_UNAME */ |
675 | | |
676 | | /* |
677 | | * Trap structures. We only allow a few of these, and send a copy of |
678 | | * each async message to each live one. Traps time out after an hour, it |
679 | | * is up to the trap receipient to keep resetting it to avoid being |
680 | | * timed out. |
681 | | */ |
682 | | /* ntp_request.c */ |
683 | | struct ctl_trap ctl_traps[CTL_MAXTRAPS]; |
684 | | int num_ctl_traps; |
685 | | |
686 | | /* |
687 | | * Type bits, for ctlsettrap() call. |
688 | | */ |
689 | 0 | #define TRAP_TYPE_CONFIG 0 /* used by configuration code */ |
690 | 0 | #define TRAP_TYPE_PRIO 1 /* priority trap */ |
691 | 0 | #define TRAP_TYPE_NONPRIO 2 /* nonpriority trap */ |
692 | | |
693 | | |
694 | | /* |
695 | | * List relating reference clock types to control message time sources. |
696 | | * Index by the reference clock type. This list will only be used iff |
697 | | * the reference clock driver doesn't set peer->sstclktype to something |
698 | | * different than CTL_SST_TS_UNSPEC. |
699 | | */ |
700 | | #ifdef REFCLOCK |
701 | | static const u_char clocktypes[] = { |
702 | | CTL_SST_TS_NTP, /* REFCLK_NONE (0) */ |
703 | | CTL_SST_TS_LOCAL, /* REFCLK_LOCALCLOCK (1) */ |
704 | | CTL_SST_TS_UHF, /* deprecated REFCLK_GPS_TRAK (2) */ |
705 | | CTL_SST_TS_HF, /* REFCLK_WWV_PST (3) */ |
706 | | CTL_SST_TS_LF, /* REFCLK_WWVB_SPECTRACOM (4) */ |
707 | | CTL_SST_TS_UHF, /* REFCLK_TRUETIME (5) */ |
708 | | CTL_SST_TS_UHF, /* REFCLK_IRIG_AUDIO (6) */ |
709 | | CTL_SST_TS_HF, /* REFCLK_CHU (7) */ |
710 | | CTL_SST_TS_LF, /* REFCLOCK_PARSE (default) (8) */ |
711 | | CTL_SST_TS_LF, /* REFCLK_GPS_MX4200 (9) */ |
712 | | CTL_SST_TS_UHF, /* REFCLK_GPS_AS2201 (10) */ |
713 | | CTL_SST_TS_UHF, /* REFCLK_GPS_ARBITER (11) */ |
714 | | CTL_SST_TS_UHF, /* REFCLK_IRIG_TPRO (12) */ |
715 | | CTL_SST_TS_ATOM, /* REFCLK_ATOM_LEITCH (13) */ |
716 | | CTL_SST_TS_LF, /* deprecated REFCLK_MSF_EES (14) */ |
717 | | CTL_SST_TS_NTP, /* not used (15) */ |
718 | | CTL_SST_TS_UHF, /* REFCLK_IRIG_BANCOMM (16) */ |
719 | | CTL_SST_TS_UHF, /* REFCLK_GPS_DATU (17) */ |
720 | | CTL_SST_TS_TELEPHONE, /* REFCLK_NIST_ACTS (18) */ |
721 | | CTL_SST_TS_HF, /* REFCLK_WWV_HEATH (19) */ |
722 | | CTL_SST_TS_UHF, /* REFCLK_GPS_NMEA (20) */ |
723 | | CTL_SST_TS_UHF, /* REFCLK_GPS_VME (21) */ |
724 | | CTL_SST_TS_ATOM, /* REFCLK_ATOM_PPS (22) */ |
725 | | CTL_SST_TS_NTP, /* not used (23) */ |
726 | | CTL_SST_TS_NTP, /* not used (24) */ |
727 | | CTL_SST_TS_NTP, /* not used (25) */ |
728 | | CTL_SST_TS_UHF, /* REFCLK_GPS_HP (26) */ |
729 | | CTL_SST_TS_LF, /* REFCLK_ARCRON_MSF (27) */ |
730 | | CTL_SST_TS_UHF, /* REFCLK_SHM (28) */ |
731 | | CTL_SST_TS_UHF, /* REFCLK_PALISADE (29) */ |
732 | | CTL_SST_TS_UHF, /* REFCLK_ONCORE (30) */ |
733 | | CTL_SST_TS_UHF, /* REFCLK_JUPITER (31) */ |
734 | | CTL_SST_TS_LF, /* REFCLK_CHRONOLOG (32) */ |
735 | | CTL_SST_TS_LF, /* REFCLK_DUMBCLOCK (33) */ |
736 | | CTL_SST_TS_LF, /* REFCLK_ULINK (34) */ |
737 | | CTL_SST_TS_LF, /* REFCLK_PCF (35) */ |
738 | | CTL_SST_TS_HF, /* REFCLK_WWV (36) */ |
739 | | CTL_SST_TS_LF, /* REFCLK_FG (37) */ |
740 | | CTL_SST_TS_UHF, /* REFCLK_HOPF_SERIAL (38) */ |
741 | | CTL_SST_TS_UHF, /* REFCLK_HOPF_PCI (39) */ |
742 | | CTL_SST_TS_LF, /* REFCLK_JJY (40) */ |
743 | | CTL_SST_TS_UHF, /* REFCLK_TT560 (41) */ |
744 | | CTL_SST_TS_UHF, /* REFCLK_ZYFER (42) */ |
745 | | CTL_SST_TS_UHF, /* REFCLK_RIPENCC (43) */ |
746 | | CTL_SST_TS_UHF, /* REFCLK_NEOCLOCK4X (44) */ |
747 | | CTL_SST_TS_UHF, /* REFCLK_TSYNCPCI (45) */ |
748 | | CTL_SST_TS_UHF /* REFCLK_GPSDJSON (46) */ |
749 | | }; |
750 | | #endif /* REFCLOCK */ |
751 | | |
752 | | |
753 | | /* |
754 | | * Keyid used for authenticating write requests. |
755 | | */ |
756 | | keyid_t ctl_auth_keyid; |
757 | | |
758 | | /* |
759 | | * We keep track of the last error reported by the system internally |
760 | | */ |
761 | | static u_char ctl_sys_last_event; |
762 | | static u_char ctl_sys_num_events; |
763 | | |
764 | | |
765 | | /* |
766 | | * Statistic counters to keep track of requests and responses. |
767 | | */ |
768 | | u_long ctltimereset; /* time stats reset */ |
769 | | u_long numctlreq; /* number of requests we've received */ |
770 | | u_long numctlbadpkts; /* number of bad control packets */ |
771 | | u_long numctlresponses; /* number of resp packets sent with data */ |
772 | | u_long numctlfrags; /* number of fragments sent */ |
773 | | u_long numctlerrors; /* number of error responses sent */ |
774 | | u_long numctltooshort; /* number of too short input packets */ |
775 | | u_long numctlinputresp; /* number of responses on input */ |
776 | | u_long numctlinputfrag; /* number of fragments on input */ |
777 | | u_long numctlinputerr; /* number of input pkts with err bit set */ |
778 | | u_long numctlbadoffset; /* number of input pkts with nonzero offset */ |
779 | | u_long numctlbadversion; /* number of input pkts with unknown version */ |
780 | | u_long numctldatatooshort; /* data too short for count */ |
781 | | u_long numctlbadop; /* bad op code found in packet */ |
782 | | u_long numasyncmsgs; /* number of async messages we've sent */ |
783 | | |
784 | | /* |
785 | | * Response packet used by these routines. Also some state information |
786 | | * so that we can handle packet formatting within a common set of |
787 | | * subroutines. Note we try to enter data in place whenever possible, |
788 | | * but the need to set the more bit correctly means we occasionally |
789 | | * use the extra buffer and copy. |
790 | | */ |
791 | | static struct ntp_control rpkt; |
792 | | static u_char res_version; |
793 | | static u_char res_opcode; |
794 | | static associd_t res_associd; |
795 | | static u_short res_frags; /* datagrams in this response */ |
796 | | static int res_offset; /* offset of payload in response */ |
797 | | static u_char * datapt; |
798 | | static u_char * dataend; |
799 | | static int datalinelen; |
800 | | static int datasent; /* flag to avoid initial ", " */ |
801 | | static int datanotbinflag; |
802 | | static sockaddr_u *rmt_addr; |
803 | | static struct interface *lcl_inter; |
804 | | |
805 | | static u_char res_authenticate; |
806 | | static u_char res_authokay; |
807 | | static keyid_t res_keyid; |
808 | | |
809 | 659 | #define MAXDATALINELEN (72) |
810 | | |
811 | | static u_char res_async; /* sending async trap response? */ |
812 | | |
813 | | /* |
814 | | * Pointers for saving state when decoding request packets |
815 | | */ |
816 | | static char *reqpt; |
817 | | static char *reqend; |
818 | | |
819 | | #ifndef MIN |
820 | | #define MIN(a, b) (((a) <= (b)) ? (a) : (b)) |
821 | | #endif |
822 | | |
823 | | /* |
824 | | * init_control - initialize request data |
825 | | */ |
826 | | void |
827 | | init_control(void) |
828 | 1 | { |
829 | 1 | size_t i; |
830 | | |
831 | 1 | #ifdef HAVE_UNAME |
832 | 1 | uname(&utsnamebuf); |
833 | 1 | #endif /* HAVE_UNAME */ |
834 | | |
835 | 1 | ctl_clr_stats(); |
836 | | |
837 | 1 | ctl_auth_keyid = 0; |
838 | 1 | ctl_sys_last_event = EVNT_UNSPEC; |
839 | 1 | ctl_sys_num_events = 0; |
840 | | |
841 | 1 | num_ctl_traps = 0; |
842 | 4 | for (i = 0; i < COUNTOF(ctl_traps); i++) |
843 | 3 | ctl_traps[i].tr_flags = 0; |
844 | 1 | } |
845 | | |
846 | | |
847 | | /* |
848 | | * ctl_error - send an error response for the current request |
849 | | */ |
850 | | static void |
851 | | ctl_error( |
852 | | u_char errcode |
853 | | ) |
854 | 309 | { |
855 | 309 | size_t maclen; |
856 | | |
857 | 309 | numctlerrors++; |
858 | 309 | DPRINTF(3, ("sending control error %u\n", errcode)); |
859 | | |
860 | | /* |
861 | | * Fill in the fields. We assume rpkt.sequence and rpkt.associd |
862 | | * have already been filled in. |
863 | | */ |
864 | 309 | rpkt.r_m_e_op = (u_char)CTL_RESPONSE | CTL_ERROR | |
865 | 309 | (res_opcode & CTL_OP_MASK); |
866 | 309 | rpkt.status = htons((u_short)(errcode << 8) & 0xff00); |
867 | 309 | rpkt.count = 0; |
868 | | |
869 | | /* |
870 | | * send packet and bump counters |
871 | | */ |
872 | 309 | if (res_authenticate && sys_authenticate) { |
873 | 51 | maclen = authencrypt(res_keyid, (u_int32 *)&rpkt, |
874 | 51 | CTL_HEADER_LEN); |
875 | 51 | sendpkt(rmt_addr, lcl_inter, -2, (void *)&rpkt, |
876 | 51 | CTL_HEADER_LEN + maclen); |
877 | 51 | } else |
878 | 258 | sendpkt(rmt_addr, lcl_inter, -3, (void *)&rpkt, |
879 | 258 | CTL_HEADER_LEN); |
880 | 309 | } |
881 | | |
882 | | int/*BOOL*/ |
883 | | is_safe_filename(const char * name) |
884 | 0 | { |
885 | | /* We need a strict validation of filenames we should write: The |
886 | | * daemon might run with special permissions and is remote |
887 | | * controllable, so we better take care what we allow as file |
888 | | * name! |
889 | | * |
890 | | * The first character must be digit or a letter from the ASCII |
891 | | * base plane or a '_' ([_A-Za-z0-9]), the following characters |
892 | | * must be from [-._+A-Za-z0-9]. |
893 | | * |
894 | | * We do not trust the character classification much here: Since |
895 | | * the NTP protocol makes no provisions for UTF-8 or local code |
896 | | * pages, we strictly require the 7bit ASCII code page. |
897 | | * |
898 | | * The following table is a packed bit field of 128 two-bit |
899 | | * groups. The LSB in each group tells us if a character is |
900 | | * acceptable at the first position, the MSB if the character is |
901 | | * accepted at any other position. |
902 | | * |
903 | | * This does not ensure that the file name is syntactically |
904 | | * correct (multiple dots will not work with VMS...) but it will |
905 | | * exclude potential globbing bombs and directory traversal. It |
906 | | * also rules out drive selection. (For systems that have this |
907 | | * notion, like Windows or VMS.) |
908 | | */ |
909 | 0 | static const uint32_t chclass[8] = { |
910 | 0 | 0x00000000, 0x00000000, |
911 | 0 | 0x28800000, 0x000FFFFF, |
912 | 0 | 0xFFFFFFFC, 0xC03FFFFF, |
913 | 0 | 0xFFFFFFFC, 0x003FFFFF |
914 | 0 | }; |
915 | |
|
916 | 0 | u_int widx, bidx, mask; |
917 | 0 | if ( ! (name && *name)) |
918 | 0 | return FALSE; |
919 | | |
920 | 0 | mask = 1u; |
921 | 0 | while (0 != (widx = (u_char)*name++)) { |
922 | 0 | bidx = (widx & 15) << 1; |
923 | 0 | widx = widx >> 4; |
924 | 0 | if (widx >= sizeof(chclass)/sizeof(chclass[0])) |
925 | 0 | return FALSE; |
926 | 0 | if (0 == ((chclass[widx] >> bidx) & mask)) |
927 | 0 | return FALSE; |
928 | 0 | mask = 2u; |
929 | 0 | } |
930 | 0 | return TRUE; |
931 | 0 | } |
932 | | |
933 | | |
934 | | /* |
935 | | * save_config - Implements ntpq -c "saveconfig <filename>" |
936 | | * Writes current configuration including any runtime |
937 | | * changes by ntpq's :config or config-from-file |
938 | | * |
939 | | * Note: There should be no buffer overflow or truncation in the |
940 | | * processing of file names -- both cause security problems. This is bit |
941 | | * painful to code but essential here. |
942 | | */ |
943 | | void |
944 | | save_config( |
945 | | struct recvbuf *rbufp, |
946 | | int restrict_mask |
947 | | ) |
948 | 0 | { |
949 | | /* block directory traversal by searching for characters that |
950 | | * indicate directory components in a file path. |
951 | | * |
952 | | * Conceptually we should be searching for DIRSEP in filename, |
953 | | * however Windows actually recognizes both forward and |
954 | | * backslashes as equivalent directory separators at the API |
955 | | * level. On POSIX systems we could allow '\\' but such |
956 | | * filenames are tricky to manipulate from a shell, so just |
957 | | * reject both types of slashes on all platforms. |
958 | | */ |
959 | | /* TALOS-CAN-0062: block directory traversal for VMS, too */ |
960 | 0 | static const char * illegal_in_filename = |
961 | | #if defined(VMS) |
962 | | ":[]" /* do not allow drive and path components here */ |
963 | | #elif defined(SYS_WINNT) |
964 | | ":\\/" /* path and drive separators */ |
965 | | #else |
966 | 0 | "\\/" /* separator and critical char for POSIX */ |
967 | 0 | #endif |
968 | 0 | ; |
969 | 0 | char reply[128]; |
970 | 0 | #ifdef SAVECONFIG |
971 | 0 | static const char savedconfig_eq[] = "savedconfig="; |
972 | | |
973 | | /* Build a safe open mode from the available mode flags. We want |
974 | | * to create a new file and write it in text mode (when |
975 | | * applicable -- only Windows does this...) |
976 | | */ |
977 | 0 | static const int openmode = O_CREAT | O_TRUNC | O_WRONLY |
978 | 0 | # if defined(O_EXCL) /* posix, vms */ |
979 | 0 | | O_EXCL |
980 | | # elif defined(_O_EXCL) /* windows is alway very special... */ |
981 | | | _O_EXCL |
982 | | # endif |
983 | | # if defined(_O_TEXT) /* windows, again */ |
984 | | | _O_TEXT |
985 | | #endif |
986 | 0 | ; |
987 | |
|
988 | 0 | char filespec[128]; |
989 | 0 | char filename[128]; |
990 | 0 | char fullpath[512]; |
991 | 0 | char savedconfig[sizeof(savedconfig_eq) + sizeof(filename)]; |
992 | 0 | time_t now; |
993 | 0 | int fd; |
994 | 0 | FILE *fptr; |
995 | 0 | int prc; |
996 | 0 | size_t reqlen; |
997 | 0 | #endif |
998 | |
|
999 | 0 | if (RES_NOMODIFY & restrict_mask) { |
1000 | 0 | ctl_printf("%s", "saveconfig prohibited by restrict ... nomodify"); |
1001 | 0 | ctl_flushpkt(0); |
1002 | 0 | NLOG(NLOG_SYSINFO) |
1003 | 0 | msyslog(LOG_NOTICE, |
1004 | 0 | "saveconfig from %s rejected due to nomodify restriction", |
1005 | 0 | stoa(&rbufp->recv_srcadr)); |
1006 | 0 | sys_restricted++; |
1007 | 0 | return; |
1008 | 0 | } |
1009 | | |
1010 | 0 | #ifdef SAVECONFIG |
1011 | 0 | if (NULL == saveconfigdir) { |
1012 | 0 | ctl_printf("%s", "saveconfig prohibited, no saveconfigdir configured"); |
1013 | 0 | ctl_flushpkt(0); |
1014 | 0 | NLOG(NLOG_SYSINFO) |
1015 | 0 | msyslog(LOG_NOTICE, |
1016 | 0 | "saveconfig from %s rejected, no saveconfigdir", |
1017 | 0 | stoa(&rbufp->recv_srcadr)); |
1018 | 0 | return; |
1019 | 0 | } |
1020 | | |
1021 | | /* The length checking stuff gets serious. Do not assume a NUL |
1022 | | * byte can be found, but if so, use it to calculate the needed |
1023 | | * buffer size. If the available buffer is too short, bail out; |
1024 | | * likewise if there is no file spec. (The latter will not |
1025 | | * happen when using NTPQ, but there are other ways to craft a |
1026 | | * network packet!) |
1027 | | */ |
1028 | 0 | reqlen = (size_t)(reqend - reqpt); |
1029 | 0 | if (0 != reqlen) { |
1030 | 0 | char * nulpos = (char*)memchr(reqpt, 0, reqlen); |
1031 | 0 | if (NULL != nulpos) |
1032 | 0 | reqlen = (size_t)(nulpos - reqpt); |
1033 | 0 | } |
1034 | 0 | if (0 == reqlen) |
1035 | 0 | return; |
1036 | 0 | if (reqlen >= sizeof(filespec)) { |
1037 | 0 | ctl_printf("saveconfig exceeded maximum raw name length (%u)", |
1038 | 0 | (u_int)sizeof(filespec)); |
1039 | 0 | ctl_flushpkt(0); |
1040 | 0 | msyslog(LOG_NOTICE, |
1041 | 0 | "saveconfig exceeded maximum raw name length from %s", |
1042 | 0 | stoa(&rbufp->recv_srcadr)); |
1043 | 0 | return; |
1044 | 0 | } |
1045 | | |
1046 | | /* copy data directly as we exactly know the size */ |
1047 | 0 | memcpy(filespec, reqpt, reqlen); |
1048 | 0 | filespec[reqlen] = '\0'; |
1049 | | |
1050 | | /* |
1051 | | * allow timestamping of the saved config filename with |
1052 | | * strftime() format such as: |
1053 | | * ntpq -c "saveconfig ntp-%Y%m%d-%H%M%S.conf" |
1054 | | * XXX: Nice feature, but not too safe. |
1055 | | * YYY: The check for permitted characters in file names should |
1056 | | * weed out the worst. Let's hope 'strftime()' does not |
1057 | | * develop pathological problems. |
1058 | | */ |
1059 | 0 | time(&now); |
1060 | 0 | if (0 == strftime(filename, sizeof(filename), filespec, |
1061 | 0 | localtime(&now))) |
1062 | 0 | { |
1063 | | /* |
1064 | | * If we arrive here, 'strftime()' balked; most likely |
1065 | | * the buffer was too short. (Or it encounterd an empty |
1066 | | * format, or just a format that expands to an empty |
1067 | | * string.) We try to use the original name, though this |
1068 | | * is very likely to fail later if there are format |
1069 | | * specs in the string. Note that truncation cannot |
1070 | | * happen here as long as both buffers have the same |
1071 | | * size! |
1072 | | */ |
1073 | 0 | strlcpy(filename, filespec, sizeof(filename)); |
1074 | 0 | } |
1075 | | |
1076 | | /* |
1077 | | * Check the file name for sanity. This might/will rule out file |
1078 | | * names that would be legal but problematic, and it blocks |
1079 | | * directory traversal. |
1080 | | */ |
1081 | 0 | if (!is_safe_filename(filename)) { |
1082 | 0 | ctl_printf("saveconfig rejects unsafe file name '%s'", |
1083 | 0 | filename); |
1084 | 0 | ctl_flushpkt(0); |
1085 | 0 | msyslog(LOG_NOTICE, |
1086 | 0 | "saveconfig rejects unsafe file name from %s", |
1087 | 0 | stoa(&rbufp->recv_srcadr)); |
1088 | 0 | return; |
1089 | 0 | } |
1090 | | |
1091 | | /* |
1092 | | * XXX: This next test may not be needed with is_safe_filename() |
1093 | | */ |
1094 | | |
1095 | | /* block directory/drive traversal */ |
1096 | | /* TALOS-CAN-0062: block directory traversal for VMS, too */ |
1097 | 0 | if (NULL != strpbrk(filename, illegal_in_filename)) { |
1098 | 0 | snprintf(reply, sizeof(reply), |
1099 | 0 | "saveconfig does not allow directory in filename"); |
1100 | 0 | ctl_putdata(reply, strlen(reply), 0); |
1101 | 0 | ctl_flushpkt(0); |
1102 | 0 | msyslog(LOG_NOTICE, |
1103 | 0 | "saveconfig rejects unsafe file name from %s", |
1104 | 0 | stoa(&rbufp->recv_srcadr)); |
1105 | 0 | return; |
1106 | 0 | } |
1107 | | |
1108 | | /* concatenation of directory and path can cause another |
1109 | | * truncation... |
1110 | | */ |
1111 | 0 | prc = snprintf(fullpath, sizeof(fullpath), "%s%s", |
1112 | 0 | saveconfigdir, filename); |
1113 | 0 | if (prc < 0 || (size_t)prc >= sizeof(fullpath)) { |
1114 | 0 | ctl_printf("saveconfig exceeded maximum path length (%u)", |
1115 | 0 | (u_int)sizeof(fullpath)); |
1116 | 0 | ctl_flushpkt(0); |
1117 | 0 | msyslog(LOG_NOTICE, |
1118 | 0 | "saveconfig exceeded maximum path length from %s", |
1119 | 0 | stoa(&rbufp->recv_srcadr)); |
1120 | 0 | return; |
1121 | 0 | } |
1122 | | |
1123 | 0 | fd = open(fullpath, openmode, S_IRUSR | S_IWUSR); |
1124 | 0 | if (-1 == fd) |
1125 | 0 | fptr = NULL; |
1126 | 0 | else |
1127 | 0 | fptr = fdopen(fd, "w"); |
1128 | |
|
1129 | 0 | if (NULL == fptr || -1 == dump_all_config_trees(fptr, 1)) { |
1130 | 0 | ctl_printf("Unable to save configuration to file '%s': %s", |
1131 | 0 | filename, strerror(errno)); |
1132 | 0 | msyslog(LOG_ERR, |
1133 | 0 | "saveconfig %s from %s failed", filename, |
1134 | 0 | stoa(&rbufp->recv_srcadr)); |
1135 | 0 | } else { |
1136 | 0 | ctl_printf("Configuration saved to '%s'", filename); |
1137 | 0 | msyslog(LOG_NOTICE, |
1138 | 0 | "Configuration saved to '%s' (requested by %s)", |
1139 | 0 | fullpath, stoa(&rbufp->recv_srcadr)); |
1140 | | /* |
1141 | | * save the output filename in system variable |
1142 | | * savedconfig, retrieved with: |
1143 | | * ntpq -c "rv 0 savedconfig" |
1144 | | * Note: the way 'savedconfig' is defined makes overflow |
1145 | | * checks unnecessary here. |
1146 | | */ |
1147 | 0 | snprintf(savedconfig, sizeof(savedconfig), "%s%s", |
1148 | 0 | savedconfig_eq, filename); |
1149 | 0 | set_sys_var(savedconfig, strlen(savedconfig) + 1, RO); |
1150 | 0 | } |
1151 | |
|
1152 | 0 | if (NULL != fptr) |
1153 | 0 | fclose(fptr); |
1154 | | #else /* !SAVECONFIG follows */ |
1155 | | ctl_printf("%s", |
1156 | | "saveconfig unavailable, configured with --disable-saveconfig"); |
1157 | | #endif |
1158 | 0 | ctl_flushpkt(0); |
1159 | 0 | } |
1160 | | |
1161 | | |
1162 | | /* |
1163 | | * process_control - process an incoming control message |
1164 | | */ |
1165 | | void |
1166 | | process_control( |
1167 | | struct recvbuf *rbufp, |
1168 | | int restrict_mask |
1169 | | ) |
1170 | 1.34k | { |
1171 | 1.34k | struct ntp_control *pkt; |
1172 | 1.34k | int req_count; |
1173 | 1.34k | int req_data; |
1174 | 1.34k | const struct ctl_proc *cc; |
1175 | 1.34k | keyid_t *pkid; |
1176 | 1.34k | int properlen; |
1177 | 1.34k | size_t maclen; |
1178 | | |
1179 | 1.34k | DPRINTF(3, ("in process_control()\n")); |
1180 | | |
1181 | | /* |
1182 | | * Save the addresses for error responses |
1183 | | */ |
1184 | 1.34k | numctlreq++; |
1185 | 1.34k | rmt_addr = &rbufp->recv_srcadr; |
1186 | 1.34k | lcl_inter = rbufp->dstadr; |
1187 | 1.34k | pkt = (struct ntp_control *)&rbufp->recv_pkt; |
1188 | | |
1189 | | /* |
1190 | | * If the length is less than required for the header, or |
1191 | | * it is a response or a fragment, ignore this. |
1192 | | */ |
1193 | 1.34k | if (rbufp->recv_length < (int)CTL_HEADER_LEN |
1194 | 1.34k | || (CTL_RESPONSE | CTL_MORE | CTL_ERROR) & pkt->r_m_e_op |
1195 | 1.34k | || pkt->offset != 0) { |
1196 | 61 | DPRINTF(1, ("invalid format in control packet\n")); |
1197 | 61 | if (rbufp->recv_length < (int)CTL_HEADER_LEN) |
1198 | 32 | numctltooshort++; |
1199 | 61 | if (CTL_RESPONSE & pkt->r_m_e_op) |
1200 | 19 | numctlinputresp++; |
1201 | 61 | if (CTL_MORE & pkt->r_m_e_op) |
1202 | 21 | numctlinputfrag++; |
1203 | 61 | if (CTL_ERROR & pkt->r_m_e_op) |
1204 | 23 | numctlinputerr++; |
1205 | 61 | if (pkt->offset != 0) |
1206 | 43 | numctlbadoffset++; |
1207 | 61 | return; |
1208 | 61 | } |
1209 | 1.28k | res_version = PKT_VERSION(pkt->li_vn_mode); |
1210 | 1.28k | if (res_version > NTP_VERSION || res_version < NTP_OLDVERSION) { |
1211 | 2 | DPRINTF(1, ("unknown version %d in control packet\n", |
1212 | 2 | res_version)); |
1213 | 2 | numctlbadversion++; |
1214 | 2 | return; |
1215 | 2 | } |
1216 | | |
1217 | | /* |
1218 | | * Pull enough data from the packet to make intelligent |
1219 | | * responses |
1220 | | */ |
1221 | 1.28k | rpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, res_version, |
1222 | 1.28k | MODE_CONTROL); |
1223 | 1.28k | res_opcode = pkt->r_m_e_op; |
1224 | 1.28k | rpkt.sequence = pkt->sequence; |
1225 | 1.28k | rpkt.associd = pkt->associd; |
1226 | 1.28k | rpkt.status = 0; |
1227 | 1.28k | res_frags = 1; |
1228 | 1.28k | res_offset = 0; |
1229 | 1.28k | res_associd = htons(pkt->associd); |
1230 | 1.28k | res_async = FALSE; |
1231 | 1.28k | res_authenticate = FALSE; |
1232 | 1.28k | res_keyid = 0; |
1233 | 1.28k | res_authokay = FALSE; |
1234 | 1.28k | req_count = (int)ntohs(pkt->count); |
1235 | 1.28k | datanotbinflag = FALSE; |
1236 | 1.28k | datalinelen = 0; |
1237 | 1.28k | datasent = 0; |
1238 | 1.28k | datapt = rpkt.u.data; |
1239 | 1.28k | dataend = &rpkt.u.data[CTL_MAX_DATA_LEN]; |
1240 | | |
1241 | 1.28k | if ((rbufp->recv_length & 0x3) != 0) |
1242 | 32 | DPRINTF(3, ("Control packet length %d unrounded\n", |
1243 | 1.28k | rbufp->recv_length)); |
1244 | | |
1245 | | /* |
1246 | | * We're set up now. Make sure we've got at least enough |
1247 | | * incoming data space to match the count. |
1248 | | */ |
1249 | 1.28k | req_data = rbufp->recv_length - CTL_HEADER_LEN; |
1250 | 1.28k | if (req_data < req_count || rbufp->recv_length & 0x3) { |
1251 | 59 | ctl_error(CERR_BADFMT); |
1252 | 59 | numctldatatooshort++; |
1253 | 59 | return; |
1254 | 59 | } |
1255 | | |
1256 | 1.22k | properlen = req_count + CTL_HEADER_LEN; |
1257 | | /* round up proper len to a 8 octet boundary */ |
1258 | | |
1259 | 1.22k | properlen = (properlen + 7) & ~7; |
1260 | 1.22k | maclen = rbufp->recv_length - properlen; |
1261 | 1.22k | if ((rbufp->recv_length & 3) == 0 && |
1262 | 1.22k | maclen >= MIN_MAC_LEN && maclen <= MAX_MAC_LEN && |
1263 | 1.22k | sys_authenticate) { |
1264 | 137 | res_authenticate = TRUE; |
1265 | 137 | pkid = (void *)((char *)pkt + properlen); |
1266 | 137 | res_keyid = ntohl(*pkid); |
1267 | 137 | DPRINTF(3, ("recv_len %d, properlen %d, wants auth with keyid %08x, MAC length=%zu\n", |
1268 | 137 | rbufp->recv_length, properlen, res_keyid, |
1269 | 137 | maclen)); |
1270 | | |
1271 | 137 | if (!authistrustedip(res_keyid, &rbufp->recv_srcadr)) |
1272 | 137 | DPRINTF(3, ("invalid keyid %08x\n", res_keyid)); |
1273 | 0 | else if (authdecrypt(res_keyid, (u_int32 *)pkt, |
1274 | 0 | rbufp->recv_length - maclen, |
1275 | 0 | maclen)) { |
1276 | 0 | res_authokay = TRUE; |
1277 | 0 | DPRINTF(3, ("authenticated okay\n")); |
1278 | 0 | } else { |
1279 | 0 | res_keyid = 0; |
1280 | 0 | DPRINTF(3, ("authentication failed\n")); |
1281 | 0 | } |
1282 | 137 | } |
1283 | | |
1284 | | /* |
1285 | | * Set up translate pointers |
1286 | | */ |
1287 | 1.22k | reqpt = (char *)pkt->u.data; |
1288 | 1.22k | reqend = reqpt + req_count; |
1289 | | |
1290 | | /* |
1291 | | * Look for the opcode processor |
1292 | | */ |
1293 | 10.1k | for (cc = control_codes; cc->control_code != NO_REQUEST; cc++) { |
1294 | 10.1k | if (cc->control_code == res_opcode) { |
1295 | 1.21k | DPRINTF(3, ("opcode %d, found command handler\n", |
1296 | 1.21k | res_opcode)); |
1297 | 1.21k | if (cc->flags == AUTH |
1298 | 1.21k | && (!res_authokay |
1299 | 9 | || res_keyid != ctl_auth_keyid)) { |
1300 | 9 | ctl_error(CERR_PERMISSION); |
1301 | 9 | return; |
1302 | 9 | } |
1303 | 1.20k | (cc->handler)(rbufp, restrict_mask); |
1304 | 1.20k | return; |
1305 | 1.21k | } |
1306 | 10.1k | } |
1307 | | |
1308 | | /* |
1309 | | * Can't find this one, return an error. |
1310 | | */ |
1311 | 8 | numctlbadop++; |
1312 | 8 | ctl_error(CERR_BADOP); |
1313 | 8 | return; |
1314 | 1.22k | } |
1315 | | |
1316 | | |
1317 | | /* |
1318 | | * ctlpeerstatus - return a status word for this peer |
1319 | | */ |
1320 | | u_short |
1321 | | ctlpeerstatus( |
1322 | | register struct peer *p |
1323 | | ) |
1324 | 0 | { |
1325 | 0 | u_short status; |
1326 | |
|
1327 | 0 | status = p->status; |
1328 | 0 | if (FLAG_CONFIG & p->flags) |
1329 | 0 | status |= CTL_PST_CONFIG; |
1330 | 0 | if (p->keyid) |
1331 | 0 | status |= CTL_PST_AUTHENABLE; |
1332 | 0 | if (FLAG_AUTHENTIC & p->flags) |
1333 | 0 | status |= CTL_PST_AUTHENTIC; |
1334 | 0 | if (p->reach) |
1335 | 0 | status |= CTL_PST_REACH; |
1336 | 0 | if (MDF_TXONLY_MASK & p->cast_flags) |
1337 | 0 | status |= CTL_PST_BCAST; |
1338 | |
|
1339 | 0 | return CTL_PEER_STATUS(status, p->num_events, p->last_event); |
1340 | 0 | } |
1341 | | |
1342 | | |
1343 | | /* |
1344 | | * ctlclkstatus - return a status word for this clock |
1345 | | */ |
1346 | | #ifdef REFCLOCK |
1347 | | static u_short |
1348 | | ctlclkstatus( |
1349 | | struct refclockstat *pcs |
1350 | | ) |
1351 | 0 | { |
1352 | 0 | return CTL_PEER_STATUS(0, pcs->lastevent, pcs->currentstatus); |
1353 | 0 | } |
1354 | | #endif |
1355 | | |
1356 | | |
1357 | | /* |
1358 | | * ctlsysstatus - return the system status word |
1359 | | */ |
1360 | | u_short |
1361 | | ctlsysstatus(void) |
1362 | 161 | { |
1363 | 161 | register u_char this_clock; |
1364 | | |
1365 | 161 | this_clock = CTL_SST_TS_UNSPEC; |
1366 | 161 | #ifdef REFCLOCK |
1367 | 161 | if (sys_peer != NULL) { |
1368 | 0 | if (CTL_SST_TS_UNSPEC != sys_peer->sstclktype) |
1369 | 0 | this_clock = sys_peer->sstclktype; |
1370 | 0 | else if (sys_peer->refclktype < COUNTOF(clocktypes)) |
1371 | 0 | this_clock = clocktypes[sys_peer->refclktype]; |
1372 | 0 | } |
1373 | | #else /* REFCLOCK */ |
1374 | | if (sys_peer != 0) |
1375 | | this_clock = CTL_SST_TS_NTP; |
1376 | | #endif /* REFCLOCK */ |
1377 | 161 | return CTL_SYS_STATUS(sys_leap, this_clock, ctl_sys_num_events, |
1378 | 161 | ctl_sys_last_event); |
1379 | 161 | } |
1380 | | |
1381 | | |
1382 | | /* |
1383 | | * ctl_flushpkt - write out the current packet and prepare |
1384 | | * another if necessary. |
1385 | | */ |
1386 | | static void |
1387 | | ctl_flushpkt( |
1388 | | u_char more |
1389 | | ) |
1390 | 109 | { |
1391 | 109 | size_t i; |
1392 | 109 | size_t dlen; |
1393 | 109 | size_t sendlen; |
1394 | 109 | size_t maclen; |
1395 | 109 | size_t totlen; |
1396 | 109 | keyid_t keyid; |
1397 | | |
1398 | 109 | dlen = datapt - rpkt.u.data; |
1399 | 109 | if (!more && datanotbinflag && dlen + 2 < CTL_MAX_DATA_LEN) { |
1400 | | /* |
1401 | | * Big hack, output a trailing \r\n |
1402 | | */ |
1403 | 94 | *datapt++ = '\r'; |
1404 | 94 | *datapt++ = '\n'; |
1405 | 94 | dlen += 2; |
1406 | 94 | } |
1407 | 109 | sendlen = dlen + CTL_HEADER_LEN; |
1408 | | |
1409 | | /* |
1410 | | * Pad to a multiple of 32 bits |
1411 | | */ |
1412 | 138 | while (sendlen & 0x3) { |
1413 | 29 | *datapt++ = '\0'; |
1414 | 29 | sendlen++; |
1415 | 29 | } |
1416 | | |
1417 | | /* |
1418 | | * Fill in the packet with the current info |
1419 | | */ |
1420 | 109 | rpkt.r_m_e_op = CTL_RESPONSE | more | |
1421 | 109 | (res_opcode & CTL_OP_MASK); |
1422 | 109 | rpkt.count = htons((u_short)dlen); |
1423 | 109 | rpkt.offset = htons((u_short)res_offset); |
1424 | 109 | if (res_async) { |
1425 | 0 | for (i = 0; i < COUNTOF(ctl_traps); i++) { |
1426 | 0 | if (TRAP_INUSE & ctl_traps[i].tr_flags) { |
1427 | 0 | rpkt.li_vn_mode = |
1428 | 0 | PKT_LI_VN_MODE( |
1429 | 0 | sys_leap, |
1430 | 0 | ctl_traps[i].tr_version, |
1431 | 0 | MODE_CONTROL); |
1432 | 0 | rpkt.sequence = |
1433 | 0 | htons(ctl_traps[i].tr_sequence); |
1434 | 0 | sendpkt(&ctl_traps[i].tr_addr, |
1435 | 0 | ctl_traps[i].tr_localaddr, -4, |
1436 | 0 | (struct pkt *)&rpkt, sendlen); |
1437 | 0 | if (!more) |
1438 | 0 | ctl_traps[i].tr_sequence++; |
1439 | 0 | numasyncmsgs++; |
1440 | 0 | } |
1441 | 0 | } |
1442 | 109 | } else { |
1443 | 109 | if (res_authenticate && sys_authenticate) { |
1444 | 22 | totlen = sendlen; |
1445 | | /* |
1446 | | * If we are going to authenticate, then there |
1447 | | * is an additional requirement that the MAC |
1448 | | * begin on a 64 bit boundary. |
1449 | | */ |
1450 | 58 | while (totlen & 7) { |
1451 | 36 | *datapt++ = '\0'; |
1452 | 36 | totlen++; |
1453 | 36 | } |
1454 | 22 | keyid = htonl(res_keyid); |
1455 | 22 | memcpy(datapt, &keyid, sizeof(keyid)); |
1456 | 22 | maclen = authencrypt(res_keyid, |
1457 | 22 | (u_int32 *)&rpkt, totlen); |
1458 | 22 | sendpkt(rmt_addr, lcl_inter, -5, |
1459 | 22 | (struct pkt *)&rpkt, totlen + maclen); |
1460 | 87 | } else { |
1461 | 87 | sendpkt(rmt_addr, lcl_inter, -6, |
1462 | 87 | (struct pkt *)&rpkt, sendlen); |
1463 | 87 | } |
1464 | 109 | if (more) |
1465 | 0 | numctlfrags++; |
1466 | 109 | else |
1467 | 109 | numctlresponses++; |
1468 | 109 | } |
1469 | | |
1470 | | /* |
1471 | | * Set us up for another go around. |
1472 | | */ |
1473 | 109 | res_frags++; |
1474 | 109 | res_offset += dlen; |
1475 | 109 | datapt = rpkt.u.data; |
1476 | 109 | } |
1477 | | |
1478 | | |
1479 | | /* -------------------------------------------------------------------- |
1480 | | * block transfer API -- stream string/data fragments into xmit buffer |
1481 | | * without additional copying |
1482 | | */ |
1483 | | |
1484 | | /* buffer descriptor: address & size of fragment |
1485 | | * 'buf' may only be NULL when 'len' is zero! |
1486 | | */ |
1487 | | typedef struct { |
1488 | | const void *buf; |
1489 | | size_t len; |
1490 | | } CtlMemBufT; |
1491 | | |
1492 | | /* put ctl data in a gather-style operation */ |
1493 | | static void |
1494 | | ctl_putdata_ex( |
1495 | | const CtlMemBufT * argv, |
1496 | | size_t argc, |
1497 | | int/*BOOL*/ bin /* set to 1 when data is binary */ |
1498 | | ) |
1499 | 753 | { |
1500 | 753 | const char * src_ptr; |
1501 | 753 | size_t src_len, cur_len, add_len, argi; |
1502 | | |
1503 | | /* text / binary preprocessing, possibly create new linefeed */ |
1504 | 753 | if (bin) { |
1505 | 0 | add_len = 0; |
1506 | 753 | } else { |
1507 | 753 | datanotbinflag = TRUE; |
1508 | 753 | add_len = 3; |
1509 | | |
1510 | 753 | if (datasent) { |
1511 | 659 | *datapt++ = ','; |
1512 | 659 | datalinelen++; |
1513 | | |
1514 | | /* sum up total length */ |
1515 | 2.71k | for (argi = 0, src_len = 0; argi < argc; ++argi) |
1516 | 2.05k | src_len += argv[argi].len; |
1517 | | /* possibly start a new line, assume no size_t overflow */ |
1518 | 659 | if ((src_len + datalinelen + 1) >= MAXDATALINELEN) { |
1519 | 179 | *datapt++ = '\r'; |
1520 | 179 | *datapt++ = '\n'; |
1521 | 179 | datalinelen = 0; |
1522 | 480 | } else { |
1523 | 480 | *datapt++ = ' '; |
1524 | 480 | datalinelen++; |
1525 | 480 | } |
1526 | 659 | } |
1527 | 753 | } |
1528 | | |
1529 | | /* now stream out all buffers */ |
1530 | 3.12k | for (argi = 0; argi < argc; ++argi) { |
1531 | 2.37k | src_ptr = argv[argi].buf; |
1532 | 2.37k | src_len = argv[argi].len; |
1533 | | |
1534 | 2.37k | if ( ! (src_ptr && src_len)) |
1535 | 0 | continue; |
1536 | | |
1537 | 2.37k | cur_len = (size_t)(dataend - datapt); |
1538 | 2.37k | while ((src_len + add_len) > cur_len) { |
1539 | | /* Not enough room in this one, flush it out. */ |
1540 | 0 | if (src_len < cur_len) |
1541 | 0 | cur_len = src_len; |
1542 | |
|
1543 | 0 | memcpy(datapt, src_ptr, cur_len); |
1544 | 0 | datapt += cur_len; |
1545 | 0 | datalinelen += cur_len; |
1546 | |
|
1547 | 0 | src_ptr += cur_len; |
1548 | 0 | src_len -= cur_len; |
1549 | |
|
1550 | 0 | ctl_flushpkt(CTL_MORE); |
1551 | 0 | cur_len = (size_t)(dataend - datapt); |
1552 | 0 | } |
1553 | | |
1554 | 2.37k | memcpy(datapt, src_ptr, src_len); |
1555 | 2.37k | datapt += src_len; |
1556 | 2.37k | datalinelen += src_len; |
1557 | | |
1558 | 2.37k | datasent = TRUE; |
1559 | 2.37k | } |
1560 | 753 | } |
1561 | | |
1562 | | /* |
1563 | | * ctl_putdata - write data into the packet, fragmenting and starting |
1564 | | * another if this one is full. |
1565 | | */ |
1566 | | static void |
1567 | | ctl_putdata( |
1568 | | const char *dp, |
1569 | | unsigned int dlen, |
1570 | | int bin /* set to 1 when data is binary */ |
1571 | | ) |
1572 | 0 | { |
1573 | 0 | CtlMemBufT args[1]; |
1574 | |
|
1575 | 0 | args[0].buf = dp; |
1576 | 0 | args[0].len = dlen; |
1577 | 0 | ctl_putdata_ex(args, 1, bin); |
1578 | 0 | } |
1579 | | |
1580 | | /* |
1581 | | * ctl_putstr - write a tagged string into the response packet |
1582 | | * in the form: |
1583 | | * |
1584 | | * tag="data" |
1585 | | * |
1586 | | * len is the data length excluding the NUL terminator, |
1587 | | * as in ctl_putstr("var", "value", strlen("value")); |
1588 | | */ |
1589 | | static void |
1590 | | ctl_putstr( |
1591 | | const char * tag, |
1592 | | const char * data, |
1593 | | size_t len |
1594 | | ) |
1595 | 112 | { |
1596 | 112 | CtlMemBufT args[4]; |
1597 | | |
1598 | 112 | args[0].buf = tag; |
1599 | 112 | args[0].len = strlen(tag); |
1600 | 112 | if (data && len) { |
1601 | 112 | args[1].buf = "=\""; |
1602 | 112 | args[1].len = 2; |
1603 | 112 | args[2].buf = data; |
1604 | 112 | args[2].len = len; |
1605 | 112 | args[3].buf = "\""; |
1606 | 112 | args[3].len = 1; |
1607 | 112 | ctl_putdata_ex(args, 4, FALSE); |
1608 | 112 | } else { |
1609 | 0 | args[1].buf = "=\"\""; |
1610 | 0 | args[1].len = 3; |
1611 | 0 | ctl_putdata_ex(args, 2, FALSE); |
1612 | 0 | } |
1613 | 112 | } |
1614 | | |
1615 | | |
1616 | | /* |
1617 | | * ctl_putunqstr - write a tagged string into the response packet |
1618 | | * in the form: |
1619 | | * |
1620 | | * tag=data |
1621 | | * |
1622 | | * len is the data length excluding the NUL terminator. |
1623 | | * data must not contain a comma or whitespace. |
1624 | | */ |
1625 | | static void |
1626 | | ctl_putunqstr( |
1627 | | const char * tag, |
1628 | | const char * data, |
1629 | | size_t len |
1630 | | ) |
1631 | 641 | { |
1632 | 641 | CtlMemBufT args[3]; |
1633 | | |
1634 | 641 | args[0].buf = tag; |
1635 | 641 | args[0].len = strlen(tag); |
1636 | 641 | args[1].buf = "="; |
1637 | 641 | args[1].len = 1; |
1638 | 641 | if (data && len) { |
1639 | 641 | args[2].buf = data; |
1640 | 641 | args[2].len = len; |
1641 | 641 | ctl_putdata_ex(args, 3, FALSE); |
1642 | 641 | } else { |
1643 | 0 | ctl_putdata_ex(args, 2, FALSE); |
1644 | 0 | } |
1645 | 641 | } |
1646 | | |
1647 | | |
1648 | | /* |
1649 | | * ctl_putdblf - write a tagged, signed double into the response packet |
1650 | | */ |
1651 | | static void |
1652 | | ctl_putdblf( |
1653 | | const char * tag, |
1654 | | int use_f, |
1655 | | int precision, |
1656 | | double d |
1657 | | ) |
1658 | 253 | { |
1659 | 253 | char buffer[40]; |
1660 | 253 | int rc; |
1661 | | |
1662 | 253 | rc = snprintf(buffer, sizeof(buffer), |
1663 | 253 | (use_f ? "%.*f" : "%.*g"), |
1664 | 253 | precision, d); |
1665 | 253 | INSIST(rc >= 0 && (size_t)rc < sizeof(buffer)); |
1666 | 0 | ctl_putunqstr(tag, buffer, rc); |
1667 | 253 | } |
1668 | | |
1669 | | /* |
1670 | | * ctl_putuint - write a tagged unsigned integer into the response |
1671 | | */ |
1672 | | static void |
1673 | | ctl_putuint( |
1674 | | const char *tag, |
1675 | | u_long uval |
1676 | | ) |
1677 | 199 | { |
1678 | 199 | char buffer[24]; /* needs to fit for 64 bits! */ |
1679 | 199 | int rc; |
1680 | | |
1681 | 199 | rc = snprintf(buffer, sizeof(buffer), "%lu", uval); |
1682 | 199 | INSIST(rc >= 0 && (size_t)rc < sizeof(buffer)); |
1683 | 0 | ctl_putunqstr(tag, buffer, rc); |
1684 | 199 | } |
1685 | | |
1686 | | /* |
1687 | | * ctl_putcal - write a decoded calendar data into the response. |
1688 | | * only used with AUTOKEY currently, so compiled conditional |
1689 | | */ |
1690 | | #ifdef AUTOKEY |
1691 | | static void |
1692 | | ctl_putcal( |
1693 | | const char *tag, |
1694 | | const struct calendar *pcal |
1695 | | ) |
1696 | | { |
1697 | | char buffer[16]; |
1698 | | int rc; |
1699 | | |
1700 | | rc = snprintf(buffer, sizeof(buffer), |
1701 | | "%04d%02d%02d%02d%02d", |
1702 | | pcal->year, pcal->month, pcal->monthday, |
1703 | | pcal->hour, pcal->minute |
1704 | | ); |
1705 | | INSIST(rc >= 0 && (size_t)rc < sizeof(buffer)); |
1706 | | ctl_putunqstr(tag, buffer, rc); |
1707 | | } |
1708 | | #endif |
1709 | | |
1710 | | /* |
1711 | | * ctl_putfs - write a decoded filestamp into the response |
1712 | | */ |
1713 | | static void |
1714 | | ctl_putfs( |
1715 | | const char *tag, |
1716 | | tstamp_t uval |
1717 | | ) |
1718 | 0 | { |
1719 | 0 | char buffer[16]; |
1720 | 0 | int rc; |
1721 | |
|
1722 | 0 | time_t fstamp = (time_t)uval - JAN_1970; |
1723 | 0 | struct tm *tm = gmtime(&fstamp); |
1724 | |
|
1725 | 0 | if (NULL == tm) |
1726 | 0 | return; |
1727 | | |
1728 | 0 | rc = snprintf(buffer, sizeof(buffer), |
1729 | 0 | "%04d%02d%02d%02d%02d", |
1730 | 0 | tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, |
1731 | 0 | tm->tm_hour, tm->tm_min); |
1732 | 0 | INSIST(rc >= 0 && (size_t)rc < sizeof(buffer)); |
1733 | 0 | ctl_putunqstr(tag, buffer, rc); |
1734 | 0 | } |
1735 | | |
1736 | | |
1737 | | /* |
1738 | | * ctl_puthex - write a tagged unsigned integer, in hex, into the |
1739 | | * response |
1740 | | */ |
1741 | | static void |
1742 | | ctl_puthex( |
1743 | | const char *tag, |
1744 | | u_long uval |
1745 | | ) |
1746 | 0 | { |
1747 | 0 | char buffer[24]; /* must fit 64bit int! */ |
1748 | 0 | int rc; |
1749 | |
|
1750 | 0 | rc = snprintf(buffer, sizeof(buffer), "0x%lx", uval); |
1751 | 0 | INSIST(rc >= 0 && (size_t)rc < sizeof(buffer)); |
1752 | 0 | ctl_putunqstr(tag, buffer, rc); |
1753 | 0 | } |
1754 | | |
1755 | | |
1756 | | /* |
1757 | | * ctl_putint - write a tagged signed integer into the response |
1758 | | */ |
1759 | | static void |
1760 | | ctl_putint( |
1761 | | const char *tag, |
1762 | | long ival |
1763 | | ) |
1764 | 35 | { |
1765 | 35 | char buffer[24]; /*must fit 64bit int */ |
1766 | 35 | int rc; |
1767 | | |
1768 | 35 | rc = snprintf(buffer, sizeof(buffer), "%ld", ival); |
1769 | 35 | INSIST(rc >= 0 && (size_t)rc < sizeof(buffer)); |
1770 | 0 | ctl_putunqstr(tag, buffer, rc); |
1771 | 35 | } |
1772 | | |
1773 | | |
1774 | | /* |
1775 | | * ctl_putts - write a tagged timestamp, in hex, into the response |
1776 | | */ |
1777 | | static void |
1778 | | ctl_putts( |
1779 | | const char *tag, |
1780 | | l_fp *ts |
1781 | | ) |
1782 | 78 | { |
1783 | 78 | char buffer[24]; |
1784 | 78 | int rc; |
1785 | | |
1786 | 78 | rc = snprintf(buffer, sizeof(buffer), |
1787 | 78 | "0x%08lx.%08lx", |
1788 | 78 | (u_long)ts->l_ui, (u_long)ts->l_uf); |
1789 | 78 | INSIST(rc >= 0 && (size_t)rc < sizeof(buffer)); |
1790 | 0 | ctl_putunqstr(tag, buffer, rc); |
1791 | 78 | } |
1792 | | |
1793 | | |
1794 | | /* |
1795 | | * ctl_putadr - write an IP address into the response |
1796 | | */ |
1797 | | static void |
1798 | | ctl_putadr( |
1799 | | const char *tag, |
1800 | | u_int32 addr32, |
1801 | | sockaddr_u *addr |
1802 | | ) |
1803 | 0 | { |
1804 | 0 | const char *cq; |
1805 | |
|
1806 | 0 | if (NULL == addr) |
1807 | 0 | cq = numtoa(addr32); |
1808 | 0 | else |
1809 | 0 | cq = stoa(addr); |
1810 | 0 | ctl_putunqstr(tag, cq, strlen(cq)); |
1811 | 0 | } |
1812 | | |
1813 | | |
1814 | | /* |
1815 | | * ctl_putrefid - send a u_int32 refid as printable text |
1816 | | */ |
1817 | | static void |
1818 | | ctl_putrefid( |
1819 | | const char * tag, |
1820 | | u_int32 refid |
1821 | | ) |
1822 | 35 | { |
1823 | 35 | size_t nc; |
1824 | | |
1825 | 35 | union { |
1826 | 35 | uint32_t w; |
1827 | 35 | uint8_t b[sizeof(uint32_t)]; |
1828 | 35 | } bytes; |
1829 | | |
1830 | 35 | bytes.w = refid; |
1831 | 175 | for (nc = 0; nc < sizeof(bytes.b) && bytes.b[nc]; ++nc) |
1832 | 140 | if ( !isprint(bytes.b[nc]) |
1833 | 140 | || isspace(bytes.b[nc]) |
1834 | 140 | || bytes.b[nc] == ',' ) |
1835 | 0 | bytes.b[nc] = '.'; |
1836 | 35 | ctl_putunqstr(tag, (const char*)bytes.b, nc); |
1837 | 35 | } |
1838 | | |
1839 | | |
1840 | | /* |
1841 | | * ctl_putarray - write a tagged eight element double array into the response |
1842 | | */ |
1843 | | static void |
1844 | | ctl_putarray( |
1845 | | const char *tag, |
1846 | | double *arr, |
1847 | | int start |
1848 | | ) |
1849 | 0 | { |
1850 | 0 | char *cp, *ep; |
1851 | 0 | char buffer[200]; |
1852 | 0 | int i, rc; |
1853 | |
|
1854 | 0 | cp = buffer; |
1855 | 0 | ep = buffer + sizeof(buffer); |
1856 | 0 | i = start; |
1857 | 0 | do { |
1858 | 0 | if (i == 0) |
1859 | 0 | i = NTP_SHIFT; |
1860 | 0 | i--; |
1861 | 0 | rc = snprintf(cp, (size_t)(ep - cp), " %.2f", arr[i] * 1e3); |
1862 | 0 | INSIST(rc >= 0 && (size_t)rc < (size_t)(ep - cp)); |
1863 | 0 | cp += rc; |
1864 | 0 | } while (i != start); |
1865 | 0 | ctl_putunqstr(tag, buffer, (size_t)(cp - buffer)); |
1866 | 0 | } |
1867 | | |
1868 | | /* |
1869 | | * ctl_printf - put a formatted string into the data buffer |
1870 | | */ |
1871 | | static void |
1872 | | ctl_printf( |
1873 | | const char * fmt, |
1874 | | ... |
1875 | | ) |
1876 | 0 | { |
1877 | 0 | static const char * ellipsis = "[...]"; |
1878 | 0 | va_list va; |
1879 | 0 | char fmtbuf[128]; |
1880 | 0 | int rc; |
1881 | |
|
1882 | 0 | va_start(va, fmt); |
1883 | 0 | rc = vsnprintf(fmtbuf, sizeof(fmtbuf), fmt, va); |
1884 | 0 | va_end(va); |
1885 | 0 | if (rc < 0 || (size_t)rc >= sizeof(fmtbuf)) |
1886 | 0 | strcpy(fmtbuf + sizeof(fmtbuf) - strlen(ellipsis) - 1, |
1887 | 0 | ellipsis); |
1888 | 0 | ctl_putdata(fmtbuf, strlen(fmtbuf), 0); |
1889 | 0 | } |
1890 | | |
1891 | | |
1892 | | /* |
1893 | | * ctl_putsys - output a system variable |
1894 | | */ |
1895 | | static void |
1896 | | ctl_putsys( |
1897 | | int varid |
1898 | | ) |
1899 | 892 | { |
1900 | 892 | l_fp tmp; |
1901 | 892 | char str[256]; |
1902 | 892 | u_int u; |
1903 | 892 | double kb; |
1904 | 892 | double dtemp; |
1905 | 892 | const char *ss; |
1906 | | #ifdef AUTOKEY |
1907 | | struct cert_info *cp; |
1908 | | #endif /* AUTOKEY */ |
1909 | 892 | #ifdef KERNEL_PLL |
1910 | 892 | static struct timex ntx; |
1911 | 892 | static u_long ntp_adjtime_time; |
1912 | | |
1913 | 892 | static const double to_ms_usec = |
1914 | 892 | 1.0e-3; /* usec to msec */ |
1915 | 892 | static const double to_ms_nusec = |
1916 | 892 | # ifdef STA_NANO |
1917 | 892 | 1.0e-6; /* nsec to msec */ |
1918 | | # else |
1919 | | to_ms_usec; |
1920 | | # endif |
1921 | | |
1922 | | /* |
1923 | | * CS_K_* variables depend on up-to-date output of ntp_adjtime() |
1924 | | */ |
1925 | 892 | if (CS_KERN_FIRST <= varid && varid <= CS_KERN_LAST && |
1926 | 892 | current_time != ntp_adjtime_time) { |
1927 | 0 | ZERO(ntx); |
1928 | 0 | if (ntp_adjtime(&ntx) < 0) |
1929 | 0 | msyslog(LOG_ERR, "ntp_adjtime() for mode 6 query failed: %m"); |
1930 | 0 | else |
1931 | 0 | ntp_adjtime_time = current_time; |
1932 | 0 | } |
1933 | 892 | #endif /* KERNEL_PLL */ |
1934 | | |
1935 | 892 | switch (varid) { |
1936 | | |
1937 | 36 | case CS_LEAP: |
1938 | 36 | ctl_putuint(sys_var[CS_LEAP].text, sys_leap); |
1939 | 36 | break; |
1940 | | |
1941 | 35 | case CS_STRATUM: |
1942 | 35 | ctl_putuint(sys_var[CS_STRATUM].text, sys_stratum); |
1943 | 35 | break; |
1944 | | |
1945 | 35 | case CS_PRECISION: |
1946 | 35 | ctl_putint(sys_var[CS_PRECISION].text, sys_precision); |
1947 | 35 | break; |
1948 | | |
1949 | 35 | case CS_ROOTDELAY: |
1950 | 35 | ctl_putdbl(sys_var[CS_ROOTDELAY].text, sys_rootdelay * |
1951 | 35 | 1e3); |
1952 | 35 | break; |
1953 | | |
1954 | 35 | case CS_ROOTDISPERSION: |
1955 | 35 | ctl_putdbl(sys_var[CS_ROOTDISPERSION].text, |
1956 | 35 | sys_rootdisp * 1e3); |
1957 | 35 | break; |
1958 | | |
1959 | 35 | case CS_REFID: |
1960 | 35 | if (REFID_ISTEXT(sys_stratum)) |
1961 | 35 | ctl_putrefid(sys_var[varid].text, sys_refid); |
1962 | 0 | else |
1963 | 0 | ctl_putadr(sys_var[varid].text, sys_refid, NULL); |
1964 | 35 | break; |
1965 | | |
1966 | 35 | case CS_REFTIME: |
1967 | 35 | ctl_putts(sys_var[CS_REFTIME].text, &sys_reftime); |
1968 | 35 | break; |
1969 | | |
1970 | 48 | case CS_POLL: |
1971 | 48 | ctl_putuint(sys_var[CS_POLL].text, sys_poll); |
1972 | 48 | break; |
1973 | | |
1974 | 42 | case CS_PEERID: |
1975 | 42 | if (sys_peer == NULL) |
1976 | 42 | ctl_putuint(sys_var[CS_PEERID].text, 0); |
1977 | 0 | else |
1978 | 0 | ctl_putuint(sys_var[CS_PEERID].text, |
1979 | 0 | sys_peer->associd); |
1980 | 42 | break; |
1981 | | |
1982 | 4 | case CS_PEERADR: |
1983 | 4 | if (sys_peer != NULL && sys_peer->dstadr != NULL) |
1984 | 0 | ss = sptoa(&sys_peer->srcadr); |
1985 | 4 | else |
1986 | 4 | ss = "0.0.0.0:0"; |
1987 | 4 | ctl_putunqstr(sys_var[CS_PEERADR].text, ss, strlen(ss)); |
1988 | 4 | break; |
1989 | | |
1990 | 0 | case CS_PEERMODE: |
1991 | 0 | u = (sys_peer != NULL) |
1992 | 0 | ? sys_peer->hmode |
1993 | 0 | : MODE_UNSPEC; |
1994 | 0 | ctl_putuint(sys_var[CS_PEERMODE].text, u); |
1995 | 0 | break; |
1996 | | |
1997 | 35 | case CS_OFFSET: |
1998 | 35 | ctl_putdbl6(sys_var[CS_OFFSET].text, last_offset * 1e3); |
1999 | 35 | break; |
2000 | | |
2001 | 35 | case CS_DRIFT: |
2002 | 35 | ctl_putdbl(sys_var[CS_DRIFT].text, drift_comp * 1e6); |
2003 | 35 | break; |
2004 | | |
2005 | 35 | case CS_JITTER: |
2006 | 35 | ctl_putdbl6(sys_var[CS_JITTER].text, sys_jitter * 1e3); |
2007 | 35 | break; |
2008 | | |
2009 | 35 | case CS_ERROR: |
2010 | 35 | ctl_putdbl(sys_var[CS_ERROR].text, clock_jitter * 1e3); |
2011 | 35 | break; |
2012 | | |
2013 | 43 | case CS_CLOCK: |
2014 | 43 | get_systime(&tmp); |
2015 | 43 | ctl_putts(sys_var[CS_CLOCK].text, &tmp); |
2016 | 43 | break; |
2017 | | |
2018 | 35 | case CS_PROCESSOR: |
2019 | | #ifndef HAVE_UNAME |
2020 | | ctl_putstr(sys_var[CS_PROCESSOR].text, str_processor, |
2021 | | sizeof(str_processor) - 1); |
2022 | | #else |
2023 | 35 | ctl_putstr(sys_var[CS_PROCESSOR].text, |
2024 | 35 | utsnamebuf.machine, strlen(utsnamebuf.machine)); |
2025 | 35 | #endif /* HAVE_UNAME */ |
2026 | 35 | break; |
2027 | | |
2028 | 42 | case CS_SYSTEM: |
2029 | | #ifndef HAVE_UNAME |
2030 | | ctl_putstr(sys_var[CS_SYSTEM].text, str_system, |
2031 | | sizeof(str_system) - 1); |
2032 | | #else |
2033 | 42 | snprintf(str, sizeof(str), "%s/%s", utsnamebuf.sysname, |
2034 | 42 | utsnamebuf.release); |
2035 | 42 | ctl_putstr(sys_var[CS_SYSTEM].text, str, strlen(str)); |
2036 | 42 | #endif /* HAVE_UNAME */ |
2037 | 42 | break; |
2038 | | |
2039 | 35 | case CS_VERSION: |
2040 | 35 | ctl_putstr(sys_var[CS_VERSION].text, Version, |
2041 | 35 | strlen(Version)); |
2042 | 35 | break; |
2043 | | |
2044 | 35 | case CS_STABIL: |
2045 | 35 | ctl_putdbl(sys_var[CS_STABIL].text, clock_stability * |
2046 | 35 | 1e6); |
2047 | 35 | break; |
2048 | | |
2049 | 0 | case CS_VARLIST: |
2050 | 0 | { |
2051 | 0 | char buf[CTL_MAX_DATA_LEN]; |
2052 | | //buffPointer, firstElementPointer, buffEndPointer |
2053 | 0 | char *buffp, *buffend; |
2054 | 0 | int firstVarName; |
2055 | 0 | const char *ss1; |
2056 | 0 | int len; |
2057 | 0 | const struct ctl_var *k; |
2058 | |
|
2059 | 0 | buffp = buf; |
2060 | 0 | buffend = buf + sizeof(buf); |
2061 | 0 | if (strlen(sys_var[CS_VARLIST].text) > (sizeof(buf) - 4)) |
2062 | 0 | break; /* really long var name */ |
2063 | | |
2064 | 0 | snprintf(buffp, sizeof(buf), "%s=\"",sys_var[CS_VARLIST].text); |
2065 | 0 | buffp += strlen(buffp); |
2066 | 0 | firstVarName = TRUE; |
2067 | 0 | for (k = sys_var; !(k->flags & EOV); k++) { |
2068 | 0 | if (k->flags & PADDING) |
2069 | 0 | continue; |
2070 | 0 | len = strlen(k->text); |
2071 | 0 | if (len + 1 >= buffend - buffp) |
2072 | 0 | break; |
2073 | 0 | if (!firstVarName) |
2074 | 0 | *buffp++ = ','; |
2075 | 0 | else |
2076 | 0 | firstVarName = FALSE; |
2077 | 0 | memcpy(buffp, k->text, len); |
2078 | 0 | buffp += len; |
2079 | 0 | } |
2080 | |
|
2081 | 0 | for (k = ext_sys_var; k && !(k->flags & EOV); k++) { |
2082 | 0 | if (k->flags & PADDING) |
2083 | 0 | continue; |
2084 | 0 | if (NULL == k->text) |
2085 | 0 | continue; |
2086 | 0 | ss1 = strchr(k->text, '='); |
2087 | 0 | if (NULL == ss1) |
2088 | 0 | len = strlen(k->text); |
2089 | 0 | else |
2090 | 0 | len = ss1 - k->text; |
2091 | 0 | if (len + 1 >= buffend - buffp) |
2092 | 0 | break; |
2093 | 0 | if (firstVarName) { |
2094 | 0 | *buffp++ = ','; |
2095 | 0 | firstVarName = FALSE; |
2096 | 0 | } |
2097 | 0 | memcpy(buffp, k->text,(unsigned)len); |
2098 | 0 | buffp += len; |
2099 | 0 | } |
2100 | 0 | if (2 >= buffend - buffp) |
2101 | 0 | break; |
2102 | | |
2103 | 0 | *buffp++ = '"'; |
2104 | 0 | *buffp = '\0'; |
2105 | |
|
2106 | 0 | ctl_putdata(buf, (unsigned)( buffp - buf ), 0); |
2107 | 0 | break; |
2108 | 0 | } |
2109 | | |
2110 | 36 | case CS_TAI: |
2111 | 36 | if (sys_tai > 0) |
2112 | 0 | ctl_putuint(sys_var[CS_TAI].text, sys_tai); |
2113 | 36 | break; |
2114 | | |
2115 | 35 | case CS_LEAPTAB: |
2116 | 35 | { |
2117 | 35 | leap_signature_t lsig; |
2118 | 35 | leapsec_getsig(&lsig); |
2119 | 35 | if (lsig.ttime > 0) |
2120 | 0 | ctl_putfs(sys_var[CS_LEAPTAB].text, lsig.ttime); |
2121 | 35 | break; |
2122 | 0 | } |
2123 | | |
2124 | 35 | case CS_LEAPEND: |
2125 | 35 | { |
2126 | 35 | leap_signature_t lsig; |
2127 | 35 | leapsec_getsig(&lsig); |
2128 | 35 | if (lsig.etime > 0) |
2129 | 0 | ctl_putfs(sys_var[CS_LEAPEND].text, lsig.etime); |
2130 | 35 | break; |
2131 | 0 | } |
2132 | | |
2133 | | #ifdef LEAP_SMEAR |
2134 | | case CS_LEAPSMEARINTV: |
2135 | | if (leap_smear_intv > 0) |
2136 | | ctl_putuint(sys_var[CS_LEAPSMEARINTV].text, leap_smear_intv); |
2137 | | break; |
2138 | | |
2139 | | case CS_LEAPSMEAROFFS: |
2140 | | if (leap_smear_intv > 0) |
2141 | | ctl_putdbl(sys_var[CS_LEAPSMEAROFFS].text, |
2142 | | leap_smear.doffset * 1e3); |
2143 | | break; |
2144 | | #endif /* LEAP_SMEAR */ |
2145 | | |
2146 | 38 | case CS_RATE: |
2147 | 38 | ctl_putuint(sys_var[CS_RATE].text, ntp_minpoll); |
2148 | 38 | break; |
2149 | | |
2150 | 0 | case CS_MRU_ENABLED: |
2151 | 0 | ctl_puthex(sys_var[varid].text, mon_enabled); |
2152 | 0 | break; |
2153 | | |
2154 | 0 | case CS_MRU_DEPTH: |
2155 | 0 | ctl_putuint(sys_var[varid].text, mru_entries); |
2156 | 0 | break; |
2157 | | |
2158 | 0 | case CS_MRU_MEM: |
2159 | 0 | kb = mru_entries * (sizeof(mon_entry) / 1024.); |
2160 | 0 | u = (u_int)kb; |
2161 | 0 | if (kb - u >= 0.5) |
2162 | 0 | u++; |
2163 | 0 | ctl_putuint(sys_var[varid].text, u); |
2164 | 0 | break; |
2165 | | |
2166 | 0 | case CS_MRU_DEEPEST: |
2167 | 0 | ctl_putuint(sys_var[varid].text, mru_peakentries); |
2168 | 0 | break; |
2169 | | |
2170 | 0 | case CS_MRU_MINDEPTH: |
2171 | 0 | ctl_putuint(sys_var[varid].text, mru_mindepth); |
2172 | 0 | break; |
2173 | | |
2174 | 0 | case CS_MRU_MAXAGE: |
2175 | 0 | ctl_putint(sys_var[varid].text, mru_maxage); |
2176 | 0 | break; |
2177 | | |
2178 | 0 | case CS_MRU_MAXDEPTH: |
2179 | 0 | ctl_putuint(sys_var[varid].text, mru_maxdepth); |
2180 | 0 | break; |
2181 | | |
2182 | 0 | case CS_MRU_MAXMEM: |
2183 | 0 | kb = mru_maxdepth * (sizeof(mon_entry) / 1024.); |
2184 | 0 | u = (u_int)kb; |
2185 | 0 | if (kb - u >= 0.5) |
2186 | 0 | u++; |
2187 | 0 | ctl_putuint(sys_var[varid].text, u); |
2188 | 0 | break; |
2189 | | |
2190 | 0 | case CS_SS_UPTIME: |
2191 | 0 | ctl_putuint(sys_var[varid].text, current_time); |
2192 | 0 | break; |
2193 | | |
2194 | 0 | case CS_SS_RESET: |
2195 | 0 | ctl_putuint(sys_var[varid].text, |
2196 | 0 | current_time - sys_stattime); |
2197 | 0 | break; |
2198 | | |
2199 | 0 | case CS_SS_RECEIVED: |
2200 | 0 | ctl_putuint(sys_var[varid].text, sys_received); |
2201 | 0 | break; |
2202 | | |
2203 | 0 | case CS_SS_THISVER: |
2204 | 0 | ctl_putuint(sys_var[varid].text, sys_newversion); |
2205 | 0 | break; |
2206 | | |
2207 | 0 | case CS_SS_OLDVER: |
2208 | 0 | ctl_putuint(sys_var[varid].text, sys_oldversion); |
2209 | 0 | break; |
2210 | | |
2211 | 0 | case CS_SS_BADFORMAT: |
2212 | 0 | ctl_putuint(sys_var[varid].text, sys_badlength); |
2213 | 0 | break; |
2214 | | |
2215 | 0 | case CS_SS_BADAUTH: |
2216 | 0 | ctl_putuint(sys_var[varid].text, sys_badauth); |
2217 | 0 | break; |
2218 | | |
2219 | 0 | case CS_SS_DECLINED: |
2220 | 0 | ctl_putuint(sys_var[varid].text, sys_declined); |
2221 | 0 | break; |
2222 | | |
2223 | 0 | case CS_SS_RESTRICTED: |
2224 | 0 | ctl_putuint(sys_var[varid].text, sys_restricted); |
2225 | 0 | break; |
2226 | | |
2227 | 0 | case CS_SS_LIMITED: |
2228 | 0 | ctl_putuint(sys_var[varid].text, sys_limitrejected); |
2229 | 0 | break; |
2230 | | |
2231 | 0 | case CS_SS_LAMPORT: |
2232 | 0 | ctl_putuint(sys_var[varid].text, sys_lamport); |
2233 | 0 | break; |
2234 | | |
2235 | 0 | case CS_SS_TSROUNDING: |
2236 | 0 | ctl_putuint(sys_var[varid].text, sys_tsrounding); |
2237 | 0 | break; |
2238 | | |
2239 | 0 | case CS_SS_KODSENT: |
2240 | 0 | ctl_putuint(sys_var[varid].text, sys_kodsent); |
2241 | 0 | break; |
2242 | | |
2243 | 0 | case CS_SS_PROCESSED: |
2244 | 0 | ctl_putuint(sys_var[varid].text, sys_processed); |
2245 | 0 | break; |
2246 | | |
2247 | 0 | case CS_BCASTDELAY: |
2248 | 0 | ctl_putdbl(sys_var[varid].text, sys_bdelay * 1e3); |
2249 | 0 | break; |
2250 | | |
2251 | 0 | case CS_AUTHDELAY: |
2252 | 0 | LFPTOD(&sys_authdelay, dtemp); |
2253 | 0 | ctl_putdbl(sys_var[varid].text, dtemp * 1e3); |
2254 | 0 | break; |
2255 | | |
2256 | 0 | case CS_AUTHKEYS: |
2257 | 0 | ctl_putuint(sys_var[varid].text, authnumkeys); |
2258 | 0 | break; |
2259 | | |
2260 | 0 | case CS_AUTHFREEK: |
2261 | 0 | ctl_putuint(sys_var[varid].text, authnumfreekeys); |
2262 | 0 | break; |
2263 | | |
2264 | 0 | case CS_AUTHKLOOKUPS: |
2265 | 0 | ctl_putuint(sys_var[varid].text, authkeylookups); |
2266 | 0 | break; |
2267 | | |
2268 | 0 | case CS_AUTHKNOTFOUND: |
2269 | 0 | ctl_putuint(sys_var[varid].text, authkeynotfound); |
2270 | 0 | break; |
2271 | | |
2272 | 0 | case CS_AUTHKUNCACHED: |
2273 | 0 | ctl_putuint(sys_var[varid].text, authkeyuncached); |
2274 | 0 | break; |
2275 | | |
2276 | 0 | case CS_AUTHKEXPIRED: |
2277 | 0 | ctl_putuint(sys_var[varid].text, authkeyexpired); |
2278 | 0 | break; |
2279 | | |
2280 | 0 | case CS_AUTHENCRYPTS: |
2281 | 0 | ctl_putuint(sys_var[varid].text, authencryptions); |
2282 | 0 | break; |
2283 | | |
2284 | 0 | case CS_AUTHDECRYPTS: |
2285 | 0 | ctl_putuint(sys_var[varid].text, authdecryptions); |
2286 | 0 | break; |
2287 | | |
2288 | 0 | case CS_AUTHRESET: |
2289 | 0 | ctl_putuint(sys_var[varid].text, |
2290 | 0 | current_time - auth_timereset); |
2291 | 0 | break; |
2292 | | |
2293 | | /* |
2294 | | * CTL_IF_KERNLOOP() puts a zero if the kernel loop is |
2295 | | * unavailable, otherwise calls putfunc with args. |
2296 | | */ |
2297 | | #ifndef KERNEL_PLL |
2298 | | # define CTL_IF_KERNLOOP(putfunc, args) \ |
2299 | | ctl_putint(sys_var[varid].text, 0) |
2300 | | #else |
2301 | 0 | # define CTL_IF_KERNLOOP(putfunc, args) \ |
2302 | 0 | putfunc args |
2303 | 0 | #endif |
2304 | | |
2305 | | /* |
2306 | | * CTL_IF_KERNPPS() puts a zero if either the kernel |
2307 | | * loop is unavailable, or kernel hard PPS is not |
2308 | | * active, otherwise calls putfunc with args. |
2309 | | */ |
2310 | | #ifndef KERNEL_PLL |
2311 | | # define CTL_IF_KERNPPS(putfunc, args) \ |
2312 | | ctl_putint(sys_var[varid].text, 0) |
2313 | | #else |
2314 | 0 | # define CTL_IF_KERNPPS(putfunc, args) \ |
2315 | 0 | if (0 == ntx.shift) \ |
2316 | 0 | ctl_putint(sys_var[varid].text, 0); \ |
2317 | 0 | else \ |
2318 | 0 | putfunc args /* no trailing ; */ |
2319 | 0 | #endif |
2320 | | |
2321 | 0 | case CS_K_OFFSET: |
2322 | 0 | CTL_IF_KERNLOOP( |
2323 | 0 | ctl_putdblf, |
2324 | 0 | (sys_var[varid].text, 0, -1, to_ms_nusec * ntx.offset) |
2325 | 0 | ); |
2326 | 0 | break; |
2327 | | |
2328 | 0 | case CS_K_FREQ: |
2329 | 0 | CTL_IF_KERNLOOP( |
2330 | 0 | ctl_putsfp, |
2331 | 0 | (sys_var[varid].text, ntx.freq) |
2332 | 0 | ); |
2333 | 0 | break; |
2334 | | |
2335 | 0 | case CS_K_MAXERR: |
2336 | 0 | CTL_IF_KERNLOOP( |
2337 | 0 | ctl_putdblf, |
2338 | 0 | (sys_var[varid].text, 0, 6, |
2339 | 0 | to_ms_usec * ntx.maxerror) |
2340 | 0 | ); |
2341 | 0 | break; |
2342 | | |
2343 | 0 | case CS_K_ESTERR: |
2344 | 0 | CTL_IF_KERNLOOP( |
2345 | 0 | ctl_putdblf, |
2346 | 0 | (sys_var[varid].text, 0, 6, |
2347 | 0 | to_ms_usec * ntx.esterror) |
2348 | 0 | ); |
2349 | 0 | break; |
2350 | | |
2351 | 0 | case CS_K_STFLAGS: |
2352 | | #ifndef KERNEL_PLL |
2353 | | ss = ""; |
2354 | | #else |
2355 | 0 | ss = k_st_flags(ntx.status); |
2356 | 0 | #endif |
2357 | 0 | ctl_putstr(sys_var[varid].text, ss, strlen(ss)); |
2358 | 0 | break; |
2359 | | |
2360 | 0 | case CS_K_TIMECONST: |
2361 | 0 | CTL_IF_KERNLOOP( |
2362 | 0 | ctl_putint, |
2363 | 0 | (sys_var[varid].text, ntx.constant) |
2364 | 0 | ); |
2365 | 0 | break; |
2366 | | |
2367 | 0 | case CS_K_PRECISION: |
2368 | 0 | CTL_IF_KERNLOOP( |
2369 | 0 | ctl_putdblf, |
2370 | 0 | (sys_var[varid].text, 0, 6, |
2371 | 0 | to_ms_usec * ntx.precision) |
2372 | 0 | ); |
2373 | 0 | break; |
2374 | | |
2375 | 0 | case CS_K_FREQTOL: |
2376 | 0 | CTL_IF_KERNLOOP( |
2377 | 0 | ctl_putsfp, |
2378 | 0 | (sys_var[varid].text, ntx.tolerance) |
2379 | 0 | ); |
2380 | 0 | break; |
2381 | | |
2382 | 0 | case CS_K_PPS_FREQ: |
2383 | 0 | CTL_IF_KERNPPS( |
2384 | 0 | ctl_putsfp, |
2385 | 0 | (sys_var[varid].text, ntx.ppsfreq) |
2386 | 0 | ); |
2387 | 0 | break; |
2388 | | |
2389 | 0 | case CS_K_PPS_STABIL: |
2390 | 0 | CTL_IF_KERNPPS( |
2391 | 0 | ctl_putsfp, |
2392 | 0 | (sys_var[varid].text, ntx.stabil) |
2393 | 0 | ); |
2394 | 0 | break; |
2395 | | |
2396 | 0 | case CS_K_PPS_JITTER: |
2397 | 0 | CTL_IF_KERNPPS( |
2398 | 0 | ctl_putdbl, |
2399 | 0 | (sys_var[varid].text, to_ms_nusec * ntx.jitter) |
2400 | 0 | ); |
2401 | 0 | break; |
2402 | | |
2403 | 0 | case CS_K_PPS_CALIBDUR: |
2404 | 0 | CTL_IF_KERNPPS( |
2405 | 0 | ctl_putint, |
2406 | 0 | (sys_var[varid].text, 1 << ntx.shift) |
2407 | 0 | ); |
2408 | 0 | break; |
2409 | | |
2410 | 0 | case CS_K_PPS_CALIBS: |
2411 | 0 | CTL_IF_KERNPPS( |
2412 | 0 | ctl_putint, |
2413 | 0 | (sys_var[varid].text, ntx.calcnt) |
2414 | 0 | ); |
2415 | 0 | break; |
2416 | | |
2417 | 0 | case CS_K_PPS_CALIBERRS: |
2418 | 0 | CTL_IF_KERNPPS( |
2419 | 0 | ctl_putint, |
2420 | 0 | (sys_var[varid].text, ntx.errcnt) |
2421 | 0 | ); |
2422 | 0 | break; |
2423 | | |
2424 | 0 | case CS_K_PPS_JITEXC: |
2425 | 0 | CTL_IF_KERNPPS( |
2426 | 0 | ctl_putint, |
2427 | 0 | (sys_var[varid].text, ntx.jitcnt) |
2428 | 0 | ); |
2429 | 0 | break; |
2430 | | |
2431 | 0 | case CS_K_PPS_STBEXC: |
2432 | 0 | CTL_IF_KERNPPS( |
2433 | 0 | ctl_putint, |
2434 | 0 | (sys_var[varid].text, ntx.stbcnt) |
2435 | 0 | ); |
2436 | 0 | break; |
2437 | | |
2438 | 0 | case CS_IOSTATS_RESET: |
2439 | 0 | ctl_putuint(sys_var[varid].text, |
2440 | 0 | current_time - io_timereset); |
2441 | 0 | break; |
2442 | | |
2443 | 0 | case CS_TOTAL_RBUF: |
2444 | 0 | ctl_putuint(sys_var[varid].text, total_recvbuffs()); |
2445 | 0 | break; |
2446 | | |
2447 | 0 | case CS_FREE_RBUF: |
2448 | 0 | ctl_putuint(sys_var[varid].text, free_recvbuffs()); |
2449 | 0 | break; |
2450 | | |
2451 | 0 | case CS_USED_RBUF: |
2452 | 0 | ctl_putuint(sys_var[varid].text, full_recvbuffs()); |
2453 | 0 | break; |
2454 | | |
2455 | 0 | case CS_RBUF_LOWATER: |
2456 | 0 | ctl_putuint(sys_var[varid].text, lowater_additions()); |
2457 | 0 | break; |
2458 | | |
2459 | 0 | case CS_IO_DROPPED: |
2460 | 0 | ctl_putuint(sys_var[varid].text, packets_dropped); |
2461 | 0 | break; |
2462 | | |
2463 | 0 | case CS_IO_IGNORED: |
2464 | 0 | ctl_putuint(sys_var[varid].text, packets_ignored); |
2465 | 0 | break; |
2466 | | |
2467 | 0 | case CS_IO_RECEIVED: |
2468 | 0 | ctl_putuint(sys_var[varid].text, packets_received); |
2469 | 0 | break; |
2470 | | |
2471 | 0 | case CS_IO_SENT: |
2472 | 0 | ctl_putuint(sys_var[varid].text, packets_sent); |
2473 | 0 | break; |
2474 | | |
2475 | 0 | case CS_IO_SENDFAILED: |
2476 | 0 | ctl_putuint(sys_var[varid].text, packets_notsent); |
2477 | 0 | break; |
2478 | | |
2479 | 0 | case CS_IO_WAKEUPS: |
2480 | 0 | ctl_putuint(sys_var[varid].text, handler_calls); |
2481 | 0 | break; |
2482 | | |
2483 | 0 | case CS_IO_GOODWAKEUPS: |
2484 | 0 | ctl_putuint(sys_var[varid].text, handler_pkts); |
2485 | 0 | break; |
2486 | | |
2487 | 0 | case CS_TIMERSTATS_RESET: |
2488 | 0 | ctl_putuint(sys_var[varid].text, |
2489 | 0 | current_time - timer_timereset); |
2490 | 0 | break; |
2491 | | |
2492 | 0 | case CS_TIMER_OVERRUNS: |
2493 | 0 | ctl_putuint(sys_var[varid].text, alarm_overflow); |
2494 | 0 | break; |
2495 | | |
2496 | 0 | case CS_TIMER_XMTS: |
2497 | 0 | ctl_putuint(sys_var[varid].text, timer_xmtcalls); |
2498 | 0 | break; |
2499 | | |
2500 | 8 | case CS_FUZZ: |
2501 | 8 | ctl_putdbl(sys_var[varid].text, sys_fuzz * 1e3); |
2502 | 8 | break; |
2503 | 0 | case CS_WANDER_THRESH: |
2504 | 0 | ctl_putdbl(sys_var[varid].text, wander_threshold * 1e6); |
2505 | 0 | break; |
2506 | | #ifdef AUTOKEY |
2507 | | case CS_FLAGS: |
2508 | | if (crypto_flags) |
2509 | | ctl_puthex(sys_var[CS_FLAGS].text, |
2510 | | crypto_flags); |
2511 | | break; |
2512 | | |
2513 | | case CS_DIGEST: |
2514 | | if (crypto_flags) { |
2515 | | strlcpy(str, OBJ_nid2ln(crypto_nid), |
2516 | | COUNTOF(str)); |
2517 | | ctl_putstr(sys_var[CS_DIGEST].text, str, |
2518 | | strlen(str)); |
2519 | | } |
2520 | | break; |
2521 | | |
2522 | | case CS_SIGNATURE: |
2523 | | if (crypto_flags) { |
2524 | | const EVP_MD *dp; |
2525 | | |
2526 | | dp = EVP_get_digestbynid(crypto_flags >> 16); |
2527 | | strlcpy(str, OBJ_nid2ln(EVP_MD_pkey_type(dp)), |
2528 | | COUNTOF(str)); |
2529 | | ctl_putstr(sys_var[CS_SIGNATURE].text, str, |
2530 | | strlen(str)); |
2531 | | } |
2532 | | break; |
2533 | | |
2534 | | case CS_HOST: |
2535 | | if (hostval.ptr != NULL) |
2536 | | ctl_putstr(sys_var[CS_HOST].text, hostval.ptr, |
2537 | | strlen(hostval.ptr)); |
2538 | | break; |
2539 | | |
2540 | | case CS_IDENT: |
2541 | | if (sys_ident != NULL) |
2542 | | ctl_putstr(sys_var[CS_IDENT].text, sys_ident, |
2543 | | strlen(sys_ident)); |
2544 | | break; |
2545 | | |
2546 | | case CS_CERTIF: |
2547 | | for (cp = cinfo; cp != NULL; cp = cp->link) { |
2548 | | snprintf(str, sizeof(str), "%s %s 0x%x", |
2549 | | cp->subject, cp->issuer, cp->flags); |
2550 | | ctl_putstr(sys_var[CS_CERTIF].text, str, |
2551 | | strlen(str)); |
2552 | | ctl_putcal(sys_var[CS_REVTIME].text, &(cp->last)); |
2553 | | } |
2554 | | break; |
2555 | | |
2556 | | case CS_PUBLIC: |
2557 | | if (hostval.tstamp != 0) |
2558 | | ctl_putfs(sys_var[CS_PUBLIC].text, |
2559 | | ntohl(hostval.tstamp)); |
2560 | | break; |
2561 | | #endif /* AUTOKEY */ |
2562 | | |
2563 | 70 | default: |
2564 | 70 | break; |
2565 | 892 | } |
2566 | 892 | } |
2567 | | |
2568 | | |
2569 | | /* |
2570 | | * ctl_putpeer - output a peer variable |
2571 | | */ |
2572 | | static void |
2573 | | ctl_putpeer( |
2574 | | int id, |
2575 | | struct peer *p |
2576 | | ) |
2577 | 0 | { |
2578 | 0 | char buf[CTL_MAX_DATA_LEN]; |
2579 | 0 | char *s; |
2580 | 0 | char *t; |
2581 | 0 | char *be; |
2582 | 0 | int i; |
2583 | 0 | const struct ctl_var *k; |
2584 | | #ifdef AUTOKEY |
2585 | | struct autokey *ap; |
2586 | | const EVP_MD *dp; |
2587 | | const char *str; |
2588 | | #endif /* AUTOKEY */ |
2589 | |
|
2590 | 0 | switch (id) { |
2591 | | |
2592 | 0 | case CP_CONFIG: |
2593 | 0 | ctl_putuint(peer_var[id].text, |
2594 | 0 | !(FLAG_PREEMPT & p->flags)); |
2595 | 0 | break; |
2596 | | |
2597 | 0 | case CP_AUTHENABLE: |
2598 | 0 | ctl_putuint(peer_var[id].text, !(p->keyid)); |
2599 | 0 | break; |
2600 | | |
2601 | 0 | case CP_AUTHENTIC: |
2602 | 0 | ctl_putuint(peer_var[id].text, |
2603 | 0 | !!(FLAG_AUTHENTIC & p->flags)); |
2604 | 0 | break; |
2605 | | |
2606 | 0 | case CP_SRCADR: |
2607 | 0 | ctl_putadr(peer_var[id].text, 0, &p->srcadr); |
2608 | 0 | break; |
2609 | | |
2610 | 0 | case CP_SRCPORT: |
2611 | 0 | ctl_putuint(peer_var[id].text, SRCPORT(&p->srcadr)); |
2612 | 0 | break; |
2613 | | |
2614 | 0 | case CP_SRCHOST: |
2615 | 0 | if (p->hostname != NULL) |
2616 | 0 | ctl_putstr(peer_var[id].text, p->hostname, |
2617 | 0 | strlen(p->hostname)); |
2618 | 0 | break; |
2619 | | |
2620 | 0 | case CP_DSTADR: |
2621 | 0 | ctl_putadr(peer_var[id].text, 0, |
2622 | 0 | (p->dstadr != NULL) |
2623 | 0 | ? &p->dstadr->sin |
2624 | 0 | : NULL); |
2625 | 0 | break; |
2626 | | |
2627 | 0 | case CP_DSTPORT: |
2628 | 0 | ctl_putuint(peer_var[id].text, |
2629 | 0 | (p->dstadr != NULL) |
2630 | 0 | ? SRCPORT(&p->dstadr->sin) |
2631 | 0 | : 0); |
2632 | 0 | break; |
2633 | | |
2634 | 0 | case CP_IN: |
2635 | 0 | if (p->r21 > 0.) |
2636 | 0 | ctl_putdbl(peer_var[id].text, p->r21 / 1e3); |
2637 | 0 | break; |
2638 | | |
2639 | 0 | case CP_OUT: |
2640 | 0 | if (p->r34 > 0.) |
2641 | 0 | ctl_putdbl(peer_var[id].text, p->r34 / 1e3); |
2642 | 0 | break; |
2643 | | |
2644 | 0 | case CP_RATE: |
2645 | 0 | ctl_putuint(peer_var[id].text, p->throttle); |
2646 | 0 | break; |
2647 | | |
2648 | 0 | case CP_LEAP: |
2649 | 0 | ctl_putuint(peer_var[id].text, p->leap); |
2650 | 0 | break; |
2651 | | |
2652 | 0 | case CP_HMODE: |
2653 | 0 | ctl_putuint(peer_var[id].text, p->hmode); |
2654 | 0 | break; |
2655 | | |
2656 | 0 | case CP_STRATUM: |
2657 | 0 | ctl_putuint(peer_var[id].text, p->stratum); |
2658 | 0 | break; |
2659 | | |
2660 | 0 | case CP_PPOLL: |
2661 | 0 | ctl_putuint(peer_var[id].text, p->ppoll); |
2662 | 0 | break; |
2663 | | |
2664 | 0 | case CP_HPOLL: |
2665 | 0 | ctl_putuint(peer_var[id].text, p->hpoll); |
2666 | 0 | break; |
2667 | | |
2668 | 0 | case CP_PRECISION: |
2669 | 0 | ctl_putint(peer_var[id].text, p->precision); |
2670 | 0 | break; |
2671 | | |
2672 | 0 | case CP_ROOTDELAY: |
2673 | 0 | ctl_putdbl(peer_var[id].text, p->rootdelay * 1e3); |
2674 | 0 | break; |
2675 | | |
2676 | 0 | case CP_ROOTDISPERSION: |
2677 | 0 | ctl_putdbl(peer_var[id].text, p->rootdisp * 1e3); |
2678 | 0 | break; |
2679 | | |
2680 | 0 | case CP_REFID: |
2681 | 0 | #ifdef REFCLOCK |
2682 | 0 | if (p->flags & FLAG_REFCLOCK) { |
2683 | 0 | ctl_putrefid(peer_var[id].text, p->refid); |
2684 | 0 | break; |
2685 | 0 | } |
2686 | 0 | #endif |
2687 | 0 | if (REFID_ISTEXT(p->stratum)) |
2688 | 0 | ctl_putrefid(peer_var[id].text, p->refid); |
2689 | 0 | else |
2690 | 0 | ctl_putadr(peer_var[id].text, p->refid, NULL); |
2691 | 0 | break; |
2692 | | |
2693 | 0 | case CP_REFTIME: |
2694 | 0 | ctl_putts(peer_var[id].text, &p->reftime); |
2695 | 0 | break; |
2696 | | |
2697 | 0 | case CP_ORG: |
2698 | 0 | ctl_putts(peer_var[id].text, &p->aorg); |
2699 | 0 | break; |
2700 | | |
2701 | 0 | case CP_REC: |
2702 | 0 | ctl_putts(peer_var[id].text, &p->dst); |
2703 | 0 | break; |
2704 | | |
2705 | 0 | case CP_XMT: |
2706 | 0 | if (p->xleave) |
2707 | 0 | ctl_putdbl(peer_var[id].text, p->xleave * 1e3); |
2708 | 0 | break; |
2709 | | |
2710 | 0 | case CP_BIAS: |
2711 | 0 | if (p->bias != 0.) |
2712 | 0 | ctl_putdbl(peer_var[id].text, p->bias * 1e3); |
2713 | 0 | break; |
2714 | | |
2715 | 0 | case CP_REACH: |
2716 | 0 | ctl_puthex(peer_var[id].text, p->reach); |
2717 | 0 | break; |
2718 | | |
2719 | 0 | case CP_FLASH: |
2720 | 0 | ctl_puthex(peer_var[id].text, p->flash); |
2721 | 0 | break; |
2722 | | |
2723 | 0 | case CP_TTL: |
2724 | 0 | #ifdef REFCLOCK |
2725 | 0 | if (p->flags & FLAG_REFCLOCK) { |
2726 | 0 | ctl_putuint(peer_var[id].text, p->ttl); |
2727 | 0 | break; |
2728 | 0 | } |
2729 | 0 | #endif |
2730 | 0 | if (p->ttl > 0 && p->ttl < COUNTOF(sys_ttl)) |
2731 | 0 | ctl_putint(peer_var[id].text, |
2732 | 0 | sys_ttl[p->ttl]); |
2733 | 0 | break; |
2734 | | |
2735 | 0 | case CP_UNREACH: |
2736 | 0 | ctl_putuint(peer_var[id].text, p->unreach); |
2737 | 0 | break; |
2738 | | |
2739 | 0 | case CP_TIMER: |
2740 | 0 | ctl_putuint(peer_var[id].text, |
2741 | 0 | p->nextdate - current_time); |
2742 | 0 | break; |
2743 | | |
2744 | 0 | case CP_DELAY: |
2745 | 0 | ctl_putdbl(peer_var[id].text, p->delay * 1e3); |
2746 | 0 | break; |
2747 | | |
2748 | 0 | case CP_OFFSET: |
2749 | 0 | ctl_putdbl(peer_var[id].text, p->offset * 1e3); |
2750 | 0 | break; |
2751 | | |
2752 | 0 | case CP_JITTER: |
2753 | 0 | ctl_putdbl(peer_var[id].text, p->jitter * 1e3); |
2754 | 0 | break; |
2755 | | |
2756 | 0 | case CP_DISPERSION: |
2757 | 0 | ctl_putdbl(peer_var[id].text, p->disp * 1e3); |
2758 | 0 | break; |
2759 | | |
2760 | 0 | case CP_KEYID: |
2761 | 0 | if (p->keyid > NTP_MAXKEY) |
2762 | 0 | ctl_puthex(peer_var[id].text, p->keyid); |
2763 | 0 | else |
2764 | 0 | ctl_putuint(peer_var[id].text, p->keyid); |
2765 | 0 | break; |
2766 | | |
2767 | 0 | case CP_FILTDELAY: |
2768 | 0 | ctl_putarray(peer_var[id].text, p->filter_delay, |
2769 | 0 | p->filter_nextpt); |
2770 | 0 | break; |
2771 | | |
2772 | 0 | case CP_FILTOFFSET: |
2773 | 0 | ctl_putarray(peer_var[id].text, p->filter_offset, |
2774 | 0 | p->filter_nextpt); |
2775 | 0 | break; |
2776 | | |
2777 | 0 | case CP_FILTERROR: |
2778 | 0 | ctl_putarray(peer_var[id].text, p->filter_disp, |
2779 | 0 | p->filter_nextpt); |
2780 | 0 | break; |
2781 | | |
2782 | 0 | case CP_PMODE: |
2783 | 0 | ctl_putuint(peer_var[id].text, p->pmode); |
2784 | 0 | break; |
2785 | | |
2786 | 0 | case CP_RECEIVED: |
2787 | 0 | ctl_putuint(peer_var[id].text, p->received); |
2788 | 0 | break; |
2789 | | |
2790 | 0 | case CP_SENT: |
2791 | 0 | ctl_putuint(peer_var[id].text, p->sent); |
2792 | 0 | break; |
2793 | | |
2794 | 0 | case CP_VARLIST: |
2795 | 0 | s = buf; |
2796 | 0 | be = buf + sizeof(buf); |
2797 | 0 | if (strlen(peer_var[id].text) + 4 > sizeof(buf)) |
2798 | 0 | break; /* really long var name */ |
2799 | | |
2800 | 0 | snprintf(s, sizeof(buf), "%s=\"", peer_var[id].text); |
2801 | 0 | s += strlen(s); |
2802 | 0 | t = s; |
2803 | 0 | for (k = peer_var; !(EOV & k->flags); k++) { |
2804 | 0 | if (PADDING & k->flags) |
2805 | 0 | continue; |
2806 | 0 | i = strlen(k->text); |
2807 | 0 | if (s + i + 1 >= be) |
2808 | 0 | break; |
2809 | 0 | if (s != t) |
2810 | 0 | *s++ = ','; |
2811 | 0 | memcpy(s, k->text, i); |
2812 | 0 | s += i; |
2813 | 0 | } |
2814 | 0 | if (s + 2 < be) { |
2815 | 0 | *s++ = '"'; |
2816 | 0 | *s = '\0'; |
2817 | 0 | ctl_putdata(buf, (u_int)(s - buf), 0); |
2818 | 0 | } |
2819 | 0 | break; |
2820 | | |
2821 | 0 | case CP_TIMEREC: |
2822 | 0 | ctl_putuint(peer_var[id].text, |
2823 | 0 | current_time - p->timereceived); |
2824 | 0 | break; |
2825 | | |
2826 | 0 | case CP_TIMEREACH: |
2827 | 0 | ctl_putuint(peer_var[id].text, |
2828 | 0 | current_time - p->timereachable); |
2829 | 0 | break; |
2830 | | |
2831 | 0 | case CP_BADAUTH: |
2832 | 0 | ctl_putuint(peer_var[id].text, p->badauth); |
2833 | 0 | break; |
2834 | | |
2835 | 0 | case CP_BOGUSORG: |
2836 | 0 | ctl_putuint(peer_var[id].text, p->bogusorg); |
2837 | 0 | break; |
2838 | | |
2839 | 0 | case CP_OLDPKT: |
2840 | 0 | ctl_putuint(peer_var[id].text, p->oldpkt); |
2841 | 0 | break; |
2842 | | |
2843 | 0 | case CP_SELDISP: |
2844 | 0 | ctl_putuint(peer_var[id].text, p->seldisptoolarge); |
2845 | 0 | break; |
2846 | | |
2847 | 0 | case CP_SELBROKEN: |
2848 | 0 | ctl_putuint(peer_var[id].text, p->selbroken); |
2849 | 0 | break; |
2850 | | |
2851 | 0 | case CP_CANDIDATE: |
2852 | 0 | ctl_putuint(peer_var[id].text, p->status); |
2853 | 0 | break; |
2854 | | #ifdef AUTOKEY |
2855 | | case CP_FLAGS: |
2856 | | if (p->crypto) |
2857 | | ctl_puthex(peer_var[id].text, p->crypto); |
2858 | | break; |
2859 | | |
2860 | | case CP_SIGNATURE: |
2861 | | if (p->crypto) { |
2862 | | dp = EVP_get_digestbynid(p->crypto >> 16); |
2863 | | str = OBJ_nid2ln(EVP_MD_pkey_type(dp)); |
2864 | | ctl_putstr(peer_var[id].text, str, strlen(str)); |
2865 | | } |
2866 | | break; |
2867 | | |
2868 | | case CP_HOST: |
2869 | | if (p->subject != NULL) |
2870 | | ctl_putstr(peer_var[id].text, p->subject, |
2871 | | strlen(p->subject)); |
2872 | | break; |
2873 | | |
2874 | | case CP_VALID: /* not used */ |
2875 | | break; |
2876 | | |
2877 | | case CP_INITSEQ: |
2878 | | if (NULL == (ap = p->recval.ptr)) |
2879 | | break; |
2880 | | |
2881 | | ctl_putint(peer_var[CP_INITSEQ].text, ap->seq); |
2882 | | ctl_puthex(peer_var[CP_INITKEY].text, ap->key); |
2883 | | ctl_putfs(peer_var[CP_INITTSP].text, |
2884 | | ntohl(p->recval.tstamp)); |
2885 | | break; |
2886 | | |
2887 | | case CP_IDENT: |
2888 | | if (p->ident != NULL) |
2889 | | ctl_putstr(peer_var[id].text, p->ident, |
2890 | | strlen(p->ident)); |
2891 | | break; |
2892 | | |
2893 | | |
2894 | | #endif /* AUTOKEY */ |
2895 | 0 | } |
2896 | 0 | } |
2897 | | |
2898 | | |
2899 | | #ifdef REFCLOCK |
2900 | | /* |
2901 | | * ctl_putclock - output clock variables |
2902 | | */ |
2903 | | static void |
2904 | | ctl_putclock( |
2905 | | int id, |
2906 | | struct refclockstat *pcs, |
2907 | | int mustput |
2908 | | ) |
2909 | 0 | { |
2910 | 0 | char buf[CTL_MAX_DATA_LEN]; |
2911 | 0 | char *s, *t, *be; |
2912 | 0 | const char *ss; |
2913 | 0 | int i; |
2914 | 0 | const struct ctl_var *k; |
2915 | |
|
2916 | 0 | switch (id) { |
2917 | | |
2918 | 0 | case CC_TYPE: |
2919 | 0 | if (mustput || pcs->clockdesc == NULL |
2920 | 0 | || *(pcs->clockdesc) == '\0') { |
2921 | 0 | ctl_putuint(clock_var[id].text, pcs->type); |
2922 | 0 | } |
2923 | 0 | break; |
2924 | 0 | case CC_TIMECODE: |
2925 | 0 | ctl_putstr(clock_var[id].text, |
2926 | 0 | pcs->p_lastcode, |
2927 | 0 | (unsigned)pcs->lencode); |
2928 | 0 | break; |
2929 | | |
2930 | 0 | case CC_POLL: |
2931 | 0 | ctl_putuint(clock_var[id].text, pcs->polls); |
2932 | 0 | break; |
2933 | | |
2934 | 0 | case CC_NOREPLY: |
2935 | 0 | ctl_putuint(clock_var[id].text, |
2936 | 0 | pcs->noresponse); |
2937 | 0 | break; |
2938 | | |
2939 | 0 | case CC_BADFORMAT: |
2940 | 0 | ctl_putuint(clock_var[id].text, |
2941 | 0 | pcs->badformat); |
2942 | 0 | break; |
2943 | | |
2944 | 0 | case CC_BADDATA: |
2945 | 0 | ctl_putuint(clock_var[id].text, |
2946 | 0 | pcs->baddata); |
2947 | 0 | break; |
2948 | | |
2949 | 0 | case CC_FUDGETIME1: |
2950 | 0 | if (mustput || (pcs->haveflags & CLK_HAVETIME1)) |
2951 | 0 | ctl_putdbl(clock_var[id].text, |
2952 | 0 | pcs->fudgetime1 * 1e3); |
2953 | 0 | break; |
2954 | | |
2955 | 0 | case CC_FUDGETIME2: |
2956 | 0 | if (mustput || (pcs->haveflags & CLK_HAVETIME2)) |
2957 | 0 | ctl_putdbl(clock_var[id].text, |
2958 | 0 | pcs->fudgetime2 * 1e3); |
2959 | 0 | break; |
2960 | | |
2961 | 0 | case CC_FUDGEVAL1: |
2962 | 0 | if (mustput || (pcs->haveflags & CLK_HAVEVAL1)) |
2963 | 0 | ctl_putint(clock_var[id].text, |
2964 | 0 | pcs->fudgeval1); |
2965 | 0 | break; |
2966 | | |
2967 | 0 | case CC_FUDGEVAL2: |
2968 | 0 | if (mustput || (pcs->haveflags & CLK_HAVEVAL2)) { |
2969 | 0 | if (pcs->fudgeval1 > 1) |
2970 | 0 | ctl_putadr(clock_var[id].text, |
2971 | 0 | pcs->fudgeval2, NULL); |
2972 | 0 | else |
2973 | 0 | ctl_putrefid(clock_var[id].text, |
2974 | 0 | pcs->fudgeval2); |
2975 | 0 | } |
2976 | 0 | break; |
2977 | | |
2978 | 0 | case CC_FLAGS: |
2979 | 0 | ctl_putuint(clock_var[id].text, pcs->flags); |
2980 | 0 | break; |
2981 | | |
2982 | 0 | case CC_DEVICE: |
2983 | 0 | if (pcs->clockdesc == NULL || |
2984 | 0 | *(pcs->clockdesc) == '\0') { |
2985 | 0 | if (mustput) |
2986 | 0 | ctl_putstr(clock_var[id].text, |
2987 | 0 | "", 0); |
2988 | 0 | } else { |
2989 | 0 | ctl_putstr(clock_var[id].text, |
2990 | 0 | pcs->clockdesc, |
2991 | 0 | strlen(pcs->clockdesc)); |
2992 | 0 | } |
2993 | 0 | break; |
2994 | | |
2995 | 0 | case CC_VARLIST: |
2996 | 0 | s = buf; |
2997 | 0 | be = buf + sizeof(buf); |
2998 | 0 | if (strlen(clock_var[CC_VARLIST].text) + 4 > |
2999 | 0 | sizeof(buf)) |
3000 | 0 | break; /* really long var name */ |
3001 | | |
3002 | 0 | snprintf(s, sizeof(buf), "%s=\"", |
3003 | 0 | clock_var[CC_VARLIST].text); |
3004 | 0 | s += strlen(s); |
3005 | 0 | t = s; |
3006 | |
|
3007 | 0 | for (k = clock_var; !(EOV & k->flags); k++) { |
3008 | 0 | if (PADDING & k->flags) |
3009 | 0 | continue; |
3010 | | |
3011 | 0 | i = strlen(k->text); |
3012 | 0 | if (s + i + 1 >= be) |
3013 | 0 | break; |
3014 | | |
3015 | 0 | if (s != t) |
3016 | 0 | *s++ = ','; |
3017 | 0 | memcpy(s, k->text, i); |
3018 | 0 | s += i; |
3019 | 0 | } |
3020 | |
|
3021 | 0 | for (k = pcs->kv_list; k && !(EOV & k->flags); k++) { |
3022 | 0 | if (PADDING & k->flags) |
3023 | 0 | continue; |
3024 | | |
3025 | 0 | ss = k->text; |
3026 | 0 | if (NULL == ss) |
3027 | 0 | continue; |
3028 | | |
3029 | 0 | while (*ss && *ss != '=') |
3030 | 0 | ss++; |
3031 | 0 | i = ss - k->text; |
3032 | 0 | if (s + i + 1 >= be) |
3033 | 0 | break; |
3034 | | |
3035 | 0 | if (s != t) |
3036 | 0 | *s++ = ','; |
3037 | 0 | memcpy(s, k->text, (unsigned)i); |
3038 | 0 | s += i; |
3039 | 0 | *s = '\0'; |
3040 | 0 | } |
3041 | 0 | if (s + 2 >= be) |
3042 | 0 | break; |
3043 | | |
3044 | 0 | *s++ = '"'; |
3045 | 0 | *s = '\0'; |
3046 | 0 | ctl_putdata(buf, (unsigned)(s - buf), 0); |
3047 | 0 | break; |
3048 | 0 | } |
3049 | 0 | } |
3050 | | #endif |
3051 | | |
3052 | | |
3053 | | |
3054 | | /* |
3055 | | * ctl_getitem - get the next data item from the incoming packet |
3056 | | */ |
3057 | | static const struct ctl_var * |
3058 | | ctl_getitem( |
3059 | | const struct ctl_var *var_list, |
3060 | | char **data |
3061 | | ) |
3062 | 3.03k | { |
3063 | | /* [Bug 3008] First check the packet data sanity, then search |
3064 | | * the key. This improves the consistency of result values: If |
3065 | | * the result is NULL once, it will never be EOV again for this |
3066 | | * packet; If it's EOV, it will never be NULL again until the |
3067 | | * variable is found and processed in a given 'var_list'. (That |
3068 | | * is, a result is returned that is neither NULL nor EOV). |
3069 | | */ |
3070 | 3.03k | static const struct ctl_var eol = { 0, EOV, NULL }; |
3071 | 3.03k | static char buf[128]; |
3072 | 3.03k | static u_long quiet_until; |
3073 | 3.03k | const struct ctl_var *v; |
3074 | 3.03k | char *cp; |
3075 | 3.03k | char *tp; |
3076 | | |
3077 | | /* |
3078 | | * Part One: Validate the packet state |
3079 | | */ |
3080 | | |
3081 | | /* Delete leading commas and white space */ |
3082 | 3.75k | while (reqpt < reqend && (*reqpt == ',' || |
3083 | 3.55k | isspace((unsigned char)*reqpt))) |
3084 | 712 | reqpt++; |
3085 | 3.03k | if (reqpt >= reqend) |
3086 | 198 | return NULL; |
3087 | | |
3088 | | /* Scan the string in the packet until we hit comma or |
3089 | | * EoB. Register position of first '=' on the fly. */ |
3090 | 41.3k | for (tp = NULL, cp = reqpt; cp != reqend; ++cp) { |
3091 | 40.7k | if (*cp == '=' && tp == NULL) |
3092 | 2.27k | tp = cp; |
3093 | 40.7k | if (*cp == ',') |
3094 | 2.20k | break; |
3095 | 40.7k | } |
3096 | | |
3097 | | /* Process payload, if any. */ |
3098 | 2.84k | *data = NULL; |
3099 | 2.84k | if (NULL != tp) { |
3100 | | /* eventually strip white space from argument. */ |
3101 | 2.27k | const char *plhead = tp + 1; /* skip the '=' */ |
3102 | 2.27k | const char *pltail = cp; |
3103 | 2.27k | size_t plsize; |
3104 | | |
3105 | 2.56k | while (plhead != pltail && isspace((u_char)plhead[0])) |
3106 | 290 | ++plhead; |
3107 | 2.49k | while (plhead != pltail && isspace((u_char)pltail[-1])) |
3108 | 226 | --pltail; |
3109 | | |
3110 | | /* check payload size, terminate packet on overflow */ |
3111 | 2.27k | plsize = (size_t)(pltail - plhead); |
3112 | 2.27k | if (plsize >= sizeof(buf)) |
3113 | 8 | goto badpacket; |
3114 | | |
3115 | | /* copy data, NUL terminate, and set result data ptr */ |
3116 | 2.26k | memcpy(buf, plhead, plsize); |
3117 | 2.26k | buf[plsize] = '\0'; |
3118 | 2.26k | *data = buf; |
3119 | 2.26k | } else { |
3120 | | /* no payload, current end --> current name termination */ |
3121 | 569 | tp = cp; |
3122 | 569 | } |
3123 | | |
3124 | | /* Part Two |
3125 | | * |
3126 | | * Now we're sure that the packet data itself is sane. Scan the |
3127 | | * list now. Make sure a NULL list is properly treated by |
3128 | | * returning a synthetic End-Of-Values record. We must not |
3129 | | * return NULL pointers after this point, or the behaviour would |
3130 | | * become inconsistent if called several times with different |
3131 | | * variable lists after an EoV was returned. (Such a behavior |
3132 | | * actually caused Bug 3008.) |
3133 | | */ |
3134 | | |
3135 | 2.83k | if (NULL == var_list) |
3136 | 74 | return &eol; |
3137 | | |
3138 | 30.1k | for (v = var_list; !(EOV & v->flags); ++v) |
3139 | 29.9k | if (!(PADDING & v->flags)) { |
3140 | | /* Check if the var name matches the buffer. The |
3141 | | * name is bracketed by [reqpt..tp] and not NUL |
3142 | | * terminated, and it contains no '=' char. The |
3143 | | * lookup value IS NUL-terminated but might |
3144 | | * include a '='... We have to look out for |
3145 | | * that! |
3146 | | */ |
3147 | 29.3k | const char *sp1 = reqpt; |
3148 | 29.3k | const char *sp2 = v->text; |
3149 | | |
3150 | | /* [Bug 3412] do not compare past NUL byte in name */ |
3151 | 50.3k | while ( (sp1 != tp) |
3152 | 50.3k | && ('\0' != *sp2) && (*sp1 == *sp2)) { |
3153 | 21.0k | ++sp1; |
3154 | 21.0k | ++sp2; |
3155 | 21.0k | } |
3156 | 29.3k | if (sp1 == tp && (*sp2 == '\0' || *sp2 == '=')) |
3157 | 2.64k | break; |
3158 | 29.3k | } |
3159 | | |
3160 | | /* See if we have found a valid entry or not. If found, advance |
3161 | | * the request pointer for the next round; if not, clear the |
3162 | | * data pointer so we have no dangling garbage here. |
3163 | | */ |
3164 | 2.75k | if (EOV & v->flags) |
3165 | 114 | *data = NULL; |
3166 | 2.64k | else |
3167 | 2.64k | reqpt = cp + (cp != reqend); |
3168 | 2.75k | return v; |
3169 | | |
3170 | 8 | badpacket: |
3171 | | /*TODO? somehow indicate this packet was bad, apart from syslog? */ |
3172 | 8 | numctlbadpkts++; |
3173 | 8 | NLOG(NLOG_SYSEVENT) |
3174 | 8 | if (quiet_until <= current_time) { |
3175 | 1 | quiet_until = current_time + 300; |
3176 | 1 | msyslog(LOG_WARNING, |
3177 | 1 | "Possible 'ntpdx' exploit from %s#%u (possibly spoofed)", |
3178 | 1 | stoa(rmt_addr), SRCPORT(rmt_addr)); |
3179 | 1 | } |
3180 | 8 | reqpt = reqend; /* never again for this packet! */ |
3181 | 8 | return NULL; |
3182 | 2.83k | } |
3183 | | |
3184 | | |
3185 | | /* |
3186 | | * control_unspec - response to an unspecified op-code |
3187 | | */ |
3188 | | /*ARGSUSED*/ |
3189 | | static void |
3190 | | control_unspec( |
3191 | | struct recvbuf *rbufp, |
3192 | | int restrict_mask |
3193 | | ) |
3194 | 59 | { |
3195 | 59 | struct peer *peer; |
3196 | | |
3197 | | /* |
3198 | | * What is an appropriate response to an unspecified op-code? |
3199 | | * I return no errors and no data, unless a specified assocation |
3200 | | * doesn't exist. |
3201 | | */ |
3202 | 59 | if (res_associd) { |
3203 | 50 | peer = findpeerbyassoc(res_associd); |
3204 | 50 | if (NULL == peer) { |
3205 | 50 | ctl_error(CERR_BADASSOC); |
3206 | 50 | return; |
3207 | 50 | } |
3208 | 0 | rpkt.status = htons(ctlpeerstatus(peer)); |
3209 | 0 | } else |
3210 | 9 | rpkt.status = htons(ctlsysstatus()); |
3211 | 9 | ctl_flushpkt(0); |
3212 | 9 | } |
3213 | | |
3214 | | |
3215 | | /* |
3216 | | * read_status - return either a list of associd's, or a particular |
3217 | | * peer's status. |
3218 | | */ |
3219 | | /*ARGSUSED*/ |
3220 | | static void |
3221 | | read_status( |
3222 | | struct recvbuf *rbufp, |
3223 | | int restrict_mask |
3224 | | ) |
3225 | 41 | { |
3226 | 41 | struct peer *peer; |
3227 | 41 | const u_char *cp; |
3228 | 41 | size_t n; |
3229 | | /* a_st holds association ID, status pairs alternating */ |
3230 | 41 | u_short a_st[CTL_MAX_DATA_LEN / sizeof(u_short)]; |
3231 | | |
3232 | 41 | #ifdef DEBUG |
3233 | 41 | if (debug > 2) |
3234 | 0 | printf("read_status: ID %d\n", res_associd); |
3235 | 41 | #endif |
3236 | | /* |
3237 | | * Two choices here. If the specified association ID is |
3238 | | * zero we return all known assocation ID's. Otherwise |
3239 | | * we return a bunch of stuff about the particular peer. |
3240 | | */ |
3241 | 41 | if (res_associd) { |
3242 | 36 | peer = findpeerbyassoc(res_associd); |
3243 | 36 | if (NULL == peer) { |
3244 | 36 | ctl_error(CERR_BADASSOC); |
3245 | 36 | return; |
3246 | 36 | } |
3247 | 0 | rpkt.status = htons(ctlpeerstatus(peer)); |
3248 | 0 | if (res_authokay) |
3249 | 0 | peer->num_events = 0; |
3250 | | /* |
3251 | | * For now, output everything we know about the |
3252 | | * peer. May be more selective later. |
3253 | | */ |
3254 | 0 | for (cp = def_peer_var; *cp != 0; cp++) |
3255 | 0 | ctl_putpeer((int)*cp, peer); |
3256 | 0 | ctl_flushpkt(0); |
3257 | 0 | return; |
3258 | 36 | } |
3259 | 5 | n = 0; |
3260 | 5 | rpkt.status = htons(ctlsysstatus()); |
3261 | 5 | for (peer = peer_list; peer != NULL; peer = peer->p_link) { |
3262 | 0 | a_st[n++] = htons(peer->associd); |
3263 | 0 | a_st[n++] = htons(ctlpeerstatus(peer)); |
3264 | | /* two entries each loop iteration, so n + 1 */ |
3265 | 0 | if (n + 1 >= COUNTOF(a_st)) { |
3266 | 0 | ctl_putdata((void *)a_st, n * sizeof(a_st[0]), |
3267 | 0 | 1); |
3268 | 0 | n = 0; |
3269 | 0 | } |
3270 | 0 | } |
3271 | 5 | if (n) |
3272 | 0 | ctl_putdata((void *)a_st, n * sizeof(a_st[0]), 1); |
3273 | 5 | ctl_flushpkt(0); |
3274 | 5 | } |
3275 | | |
3276 | | |
3277 | | /* |
3278 | | * read_peervars - half of read_variables() implementation |
3279 | | */ |
3280 | | static void |
3281 | | read_peervars(void) |
3282 | 33 | { |
3283 | 33 | const struct ctl_var *v; |
3284 | 33 | struct peer *peer; |
3285 | 33 | const u_char *cp; |
3286 | 33 | size_t i; |
3287 | 33 | char * valuep; |
3288 | 33 | u_char wants[CP_MAXCODE + 1]; |
3289 | 33 | u_int gotvar; |
3290 | | |
3291 | | /* |
3292 | | * Wants info for a particular peer. See if we know |
3293 | | * the guy. |
3294 | | */ |
3295 | 33 | peer = findpeerbyassoc(res_associd); |
3296 | 33 | if (NULL == peer) { |
3297 | 33 | ctl_error(CERR_BADASSOC); |
3298 | 33 | return; |
3299 | 33 | } |
3300 | 0 | rpkt.status = htons(ctlpeerstatus(peer)); |
3301 | 0 | if (res_authokay) |
3302 | 0 | peer->num_events = 0; |
3303 | 0 | ZERO(wants); |
3304 | 0 | gotvar = 0; |
3305 | 0 | while (NULL != (v = ctl_getitem(peer_var, &valuep))) { |
3306 | 0 | if (v->flags & EOV) { |
3307 | 0 | ctl_error(CERR_UNKNOWNVAR); |
3308 | 0 | return; |
3309 | 0 | } |
3310 | 0 | INSIST(v->code < COUNTOF(wants)); |
3311 | 0 | wants[v->code] = 1; |
3312 | 0 | gotvar = 1; |
3313 | 0 | } |
3314 | 0 | if (gotvar) { |
3315 | 0 | for (i = 1; i < COUNTOF(wants); i++) |
3316 | 0 | if (wants[i]) |
3317 | 0 | ctl_putpeer(i, peer); |
3318 | 0 | } else |
3319 | 0 | for (cp = def_peer_var; *cp != 0; cp++) |
3320 | 0 | ctl_putpeer((int)*cp, peer); |
3321 | 0 | ctl_flushpkt(0); |
3322 | 0 | } |
3323 | | |
3324 | | |
3325 | | /* |
3326 | | * read_sysvars - half of read_variables() implementation |
3327 | | */ |
3328 | | static void |
3329 | | read_sysvars(void) |
3330 | 132 | { |
3331 | 132 | const struct ctl_var *v; |
3332 | 132 | struct ctl_var *kv; |
3333 | 132 | u_int n; |
3334 | 132 | u_int gotvar; |
3335 | 132 | const u_char *cs; |
3336 | 132 | char * valuep; |
3337 | 132 | const char * pch; |
3338 | 132 | u_char *wants; |
3339 | 132 | size_t wants_count; |
3340 | | |
3341 | | /* |
3342 | | * Wants system variables. Figure out which he wants |
3343 | | * and give them to him. |
3344 | | */ |
3345 | 132 | rpkt.status = htons(ctlsysstatus()); |
3346 | 132 | if (res_authokay) |
3347 | 0 | ctl_sys_num_events = 0; |
3348 | 132 | wants_count = CS_MAXCODE + 1 + count_var(ext_sys_var); |
3349 | 132 | wants = emalloc_zero(wants_count); |
3350 | 132 | gotvar = 0; |
3351 | 739 | while (NULL != (v = ctl_getitem(sys_var, &valuep))) { |
3352 | 681 | if (!(EOV & v->flags)) { |
3353 | 607 | INSIST(v->code < wants_count); |
3354 | 0 | wants[v->code] = 1; |
3355 | 607 | gotvar = 1; |
3356 | 607 | } else { |
3357 | 74 | v = ctl_getitem(ext_sys_var, &valuep); |
3358 | 74 | if (NULL == v) { |
3359 | 0 | ctl_error(CERR_BADVALUE); |
3360 | 0 | free(wants); |
3361 | 0 | return; |
3362 | 0 | } |
3363 | 74 | if (EOV & v->flags) { |
3364 | 74 | ctl_error(CERR_UNKNOWNVAR); |
3365 | 74 | free(wants); |
3366 | 74 | return; |
3367 | 74 | } |
3368 | 0 | n = v->code + CS_MAXCODE + 1; |
3369 | 0 | INSIST(n < wants_count); |
3370 | 0 | wants[n] = 1; |
3371 | 0 | gotvar = 1; |
3372 | 0 | } |
3373 | 681 | } |
3374 | 58 | if (gotvar) { |
3375 | 2.16k | for (n = 1; n <= CS_MAXCODE; n++) |
3376 | 2.13k | if (wants[n]) |
3377 | 52 | ctl_putsys(n); |
3378 | 23 | for (n = 0; n + CS_MAXCODE + 1 < wants_count; n++) |
3379 | 0 | if (wants[n + CS_MAXCODE + 1]) { |
3380 | 0 | pch = ext_sys_var[n].text; |
3381 | 0 | ctl_putdata(pch, strlen(pch), 0); |
3382 | 0 | } |
3383 | 35 | } else { |
3384 | 875 | for (cs = def_sys_var; *cs != 0; cs++) |
3385 | 840 | ctl_putsys((int)*cs); |
3386 | 35 | for (kv = ext_sys_var; kv && !(EOV & kv->flags); kv++) |
3387 | 0 | if (DEF & kv->flags) |
3388 | 0 | ctl_putdata(kv->text, strlen(kv->text), |
3389 | 0 | 0); |
3390 | 35 | } |
3391 | 58 | free(wants); |
3392 | 58 | ctl_flushpkt(0); |
3393 | 58 | } |
3394 | | |
3395 | | |
3396 | | /* |
3397 | | * read_variables - return the variables the caller asks for |
3398 | | */ |
3399 | | /*ARGSUSED*/ |
3400 | | static void |
3401 | | read_variables( |
3402 | | struct recvbuf *rbufp, |
3403 | | int restrict_mask |
3404 | | ) |
3405 | 165 | { |
3406 | 165 | if (res_associd) |
3407 | 33 | read_peervars(); |
3408 | 132 | else |
3409 | 132 | read_sysvars(); |
3410 | 165 | } |
3411 | | |
3412 | | |
3413 | | /* |
3414 | | * write_variables - write into variables. We only allow leap bit |
3415 | | * writing this way. |
3416 | | */ |
3417 | | /*ARGSUSED*/ |
3418 | | static void |
3419 | | write_variables( |
3420 | | struct recvbuf *rbufp, |
3421 | | int restrict_mask |
3422 | | ) |
3423 | 0 | { |
3424 | 0 | const struct ctl_var *v; |
3425 | 0 | int ext_var; |
3426 | 0 | char *valuep; |
3427 | 0 | long val; |
3428 | 0 | size_t octets; |
3429 | 0 | char *vareqv; |
3430 | 0 | const char *t; |
3431 | 0 | char *tt; |
3432 | |
|
3433 | 0 | val = 0; |
3434 | | /* |
3435 | | * If he's trying to write into a peer tell him no way |
3436 | | */ |
3437 | 0 | if (res_associd != 0) { |
3438 | 0 | ctl_error(CERR_PERMISSION); |
3439 | 0 | return; |
3440 | 0 | } |
3441 | | |
3442 | | /* |
3443 | | * Set status |
3444 | | */ |
3445 | 0 | rpkt.status = htons(ctlsysstatus()); |
3446 | | |
3447 | | /* |
3448 | | * Look through the variables. Dump out at the first sign of |
3449 | | * trouble. |
3450 | | */ |
3451 | 0 | while ((v = ctl_getitem(sys_var, &valuep)) != NULL) { |
3452 | 0 | ext_var = 0; |
3453 | 0 | if (v->flags & EOV) { |
3454 | 0 | v = ctl_getitem(ext_sys_var, &valuep); |
3455 | 0 | if (v != NULL) { |
3456 | 0 | if (v->flags & EOV) { |
3457 | 0 | ctl_error(CERR_UNKNOWNVAR); |
3458 | 0 | return; |
3459 | 0 | } |
3460 | 0 | ext_var = 1; |
3461 | 0 | } else { |
3462 | 0 | break; |
3463 | 0 | } |
3464 | 0 | } |
3465 | 0 | if (!(v->flags & CAN_WRITE)) { |
3466 | 0 | ctl_error(CERR_PERMISSION); |
3467 | 0 | return; |
3468 | 0 | } |
3469 | | /* [bug 3565] writing makes sense only if we *have* a |
3470 | | * value in the packet! |
3471 | | */ |
3472 | 0 | if (valuep == NULL) { |
3473 | 0 | ctl_error(CERR_BADFMT); |
3474 | 0 | return; |
3475 | 0 | } |
3476 | 0 | if (!ext_var) { |
3477 | 0 | if ( !(*valuep && atoint(valuep, &val))) { |
3478 | 0 | ctl_error(CERR_BADFMT); |
3479 | 0 | return; |
3480 | 0 | } |
3481 | 0 | if ((val & ~LEAP_NOTINSYNC) != 0) { |
3482 | 0 | ctl_error(CERR_BADVALUE); |
3483 | 0 | return; |
3484 | 0 | } |
3485 | 0 | } |
3486 | | |
3487 | 0 | if (ext_var) { |
3488 | 0 | octets = strlen(v->text) + strlen(valuep) + 2; |
3489 | 0 | vareqv = emalloc(octets); |
3490 | 0 | tt = vareqv; |
3491 | 0 | t = v->text; |
3492 | 0 | while (*t && *t != '=') |
3493 | 0 | *tt++ = *t++; |
3494 | 0 | *tt++ = '='; |
3495 | 0 | memcpy(tt, valuep, 1 + strlen(valuep)); |
3496 | 0 | set_sys_var(vareqv, 1 + strlen(vareqv), v->flags); |
3497 | 0 | free(vareqv); |
3498 | 0 | } else { |
3499 | 0 | ctl_error(CERR_UNSPEC); /* really */ |
3500 | 0 | return; |
3501 | 0 | } |
3502 | 0 | } |
3503 | | |
3504 | | /* |
3505 | | * If we got anything, do it. xxx nothing to do *** |
3506 | | */ |
3507 | | /* |
3508 | | if (leapind != ~0 || leapwarn != ~0) { |
3509 | | if (!leap_setleap((int)leapind, (int)leapwarn)) { |
3510 | | ctl_error(CERR_PERMISSION); |
3511 | | return; |
3512 | | } |
3513 | | } |
3514 | | */ |
3515 | 0 | ctl_flushpkt(0); |
3516 | 0 | } |
3517 | | |
3518 | | |
3519 | | /* |
3520 | | * configure() processes ntpq :config/config-from-file, allowing |
3521 | | * generic runtime reconfiguration. |
3522 | | */ |
3523 | | static void configure( |
3524 | | struct recvbuf *rbufp, |
3525 | | int restrict_mask |
3526 | | ) |
3527 | 0 | { |
3528 | 0 | size_t data_count; |
3529 | 0 | int retval; |
3530 | | |
3531 | | /* I haven't yet implemented changes to an existing association. |
3532 | | * Hence check if the association id is 0 |
3533 | | */ |
3534 | 0 | if (res_associd != 0) { |
3535 | 0 | ctl_error(CERR_BADVALUE); |
3536 | 0 | return; |
3537 | 0 | } |
3538 | | |
3539 | 0 | if (RES_NOMODIFY & restrict_mask) { |
3540 | 0 | snprintf(remote_config.err_msg, |
3541 | 0 | sizeof(remote_config.err_msg), |
3542 | 0 | "runtime configuration prohibited by restrict ... nomodify"); |
3543 | 0 | ctl_putdata(remote_config.err_msg, |
3544 | 0 | strlen(remote_config.err_msg), 0); |
3545 | 0 | ctl_flushpkt(0); |
3546 | 0 | NLOG(NLOG_SYSINFO) |
3547 | 0 | msyslog(LOG_NOTICE, |
3548 | 0 | "runtime config from %s rejected due to nomodify restriction", |
3549 | 0 | stoa(&rbufp->recv_srcadr)); |
3550 | 0 | sys_restricted++; |
3551 | 0 | return; |
3552 | 0 | } |
3553 | | |
3554 | | /* Initialize the remote config buffer */ |
3555 | 0 | data_count = remoteconfig_cmdlength(reqpt, reqend); |
3556 | |
|
3557 | 0 | if (data_count > sizeof(remote_config.buffer) - 2) { |
3558 | 0 | snprintf(remote_config.err_msg, |
3559 | 0 | sizeof(remote_config.err_msg), |
3560 | 0 | "runtime configuration failed: request too long"); |
3561 | 0 | ctl_putdata(remote_config.err_msg, |
3562 | 0 | strlen(remote_config.err_msg), 0); |
3563 | 0 | ctl_flushpkt(0); |
3564 | 0 | msyslog(LOG_NOTICE, |
3565 | 0 | "runtime config from %s rejected: request too long", |
3566 | 0 | stoa(&rbufp->recv_srcadr)); |
3567 | 0 | return; |
3568 | 0 | } |
3569 | | /* Bug 2853 -- check if all characters were acceptable */ |
3570 | 0 | if (data_count != (size_t)(reqend - reqpt)) { |
3571 | 0 | snprintf(remote_config.err_msg, |
3572 | 0 | sizeof(remote_config.err_msg), |
3573 | 0 | "runtime configuration failed: request contains an unprintable character"); |
3574 | 0 | ctl_putdata(remote_config.err_msg, |
3575 | 0 | strlen(remote_config.err_msg), 0); |
3576 | 0 | ctl_flushpkt(0); |
3577 | 0 | msyslog(LOG_NOTICE, |
3578 | 0 | "runtime config from %s rejected: request contains an unprintable character: %0x", |
3579 | 0 | stoa(&rbufp->recv_srcadr), |
3580 | 0 | reqpt[data_count]); |
3581 | 0 | return; |
3582 | 0 | } |
3583 | | |
3584 | 0 | memcpy(remote_config.buffer, reqpt, data_count); |
3585 | | /* The buffer has no trailing linefeed or NUL right now. For |
3586 | | * logging, we do not want a newline, so we do that first after |
3587 | | * adding the necessary NUL byte. |
3588 | | */ |
3589 | 0 | remote_config.buffer[data_count] = '\0'; |
3590 | 0 | DPRINTF(1, ("Got Remote Configuration Command: %s\n", |
3591 | 0 | remote_config.buffer)); |
3592 | 0 | msyslog(LOG_NOTICE, "%s config: %s", |
3593 | 0 | stoa(&rbufp->recv_srcadr), |
3594 | 0 | remote_config.buffer); |
3595 | | |
3596 | | /* Now we have to make sure there is a NL/NUL sequence at the |
3597 | | * end of the buffer before we parse it. |
3598 | | */ |
3599 | 0 | remote_config.buffer[data_count++] = '\n'; |
3600 | 0 | remote_config.buffer[data_count] = '\0'; |
3601 | 0 | remote_config.pos = 0; |
3602 | 0 | remote_config.err_pos = 0; |
3603 | 0 | remote_config.no_errors = 0; |
3604 | 0 | config_remotely(&rbufp->recv_srcadr); |
3605 | | |
3606 | | /* |
3607 | | * Check if errors were reported. If not, output 'Config |
3608 | | * Succeeded'. Else output the error count. It would be nice |
3609 | | * to output any parser error messages. |
3610 | | */ |
3611 | 0 | if (0 == remote_config.no_errors) { |
3612 | 0 | retval = snprintf(remote_config.err_msg, |
3613 | 0 | sizeof(remote_config.err_msg), |
3614 | 0 | "Config Succeeded"); |
3615 | 0 | if (retval > 0) |
3616 | 0 | remote_config.err_pos += retval; |
3617 | 0 | } |
3618 | |
|
3619 | 0 | ctl_putdata(remote_config.err_msg, remote_config.err_pos, 0); |
3620 | 0 | ctl_flushpkt(0); |
3621 | |
|
3622 | 0 | DPRINTF(1, ("Reply: %s\n", remote_config.err_msg)); |
3623 | |
|
3624 | 0 | if (remote_config.no_errors > 0) |
3625 | 0 | msyslog(LOG_NOTICE, "%d error in %s config", |
3626 | 0 | remote_config.no_errors, |
3627 | 0 | stoa(&rbufp->recv_srcadr)); |
3628 | 0 | } |
3629 | | |
3630 | | |
3631 | | /* |
3632 | | * derive_nonce - generate client-address-specific nonce value |
3633 | | * associated with a given timestamp. |
3634 | | */ |
3635 | | static u_int32 derive_nonce( |
3636 | | sockaddr_u * addr, |
3637 | | u_int32 ts_i, |
3638 | | u_int32 ts_f |
3639 | | ) |
3640 | 124 | { |
3641 | 124 | static u_int32 salt[4]; |
3642 | 124 | static u_long last_salt_update; |
3643 | 124 | union d_tag { |
3644 | 124 | u_char digest[EVP_MAX_MD_SIZE]; |
3645 | 124 | u_int32 extract; |
3646 | 124 | } d; |
3647 | 124 | EVP_MD_CTX *ctx; |
3648 | 124 | u_int len; |
3649 | | |
3650 | 125 | while (!salt[0] || current_time - last_salt_update >= 3600) { |
3651 | 1 | salt[0] = ntp_random(); |
3652 | 1 | salt[1] = ntp_random(); |
3653 | 1 | salt[2] = ntp_random(); |
3654 | 1 | salt[3] = ntp_random(); |
3655 | 1 | last_salt_update = current_time; |
3656 | 1 | } |
3657 | | |
3658 | 124 | ctx = EVP_MD_CTX_new(); |
3659 | | # if defined(OPENSSL) && defined(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW) |
3660 | | /* [Bug 3457] set flags and don't kill them again */ |
3661 | | EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); |
3662 | | EVP_DigestInit_ex(ctx, EVP_get_digestbynid(NID_md5), NULL); |
3663 | | # else |
3664 | 124 | EVP_DigestInit(ctx, EVP_get_digestbynid(NID_md5)); |
3665 | 124 | # endif |
3666 | 124 | EVP_DigestUpdate(ctx, salt, sizeof(salt)); |
3667 | 124 | EVP_DigestUpdate(ctx, &ts_i, sizeof(ts_i)); |
3668 | 124 | EVP_DigestUpdate(ctx, &ts_f, sizeof(ts_f)); |
3669 | 124 | if (IS_IPV4(addr)) |
3670 | 124 | EVP_DigestUpdate(ctx, &SOCK_ADDR4(addr), |
3671 | 123 | sizeof(SOCK_ADDR4(addr))); |
3672 | 123 | else |
3673 | 124 | EVP_DigestUpdate(ctx, &SOCK_ADDR6(addr), |
3674 | 124 | sizeof(SOCK_ADDR6(addr))); |
3675 | 124 | EVP_DigestUpdate(ctx, &NSRCPORT(addr), sizeof(NSRCPORT(addr))); |
3676 | 124 | EVP_DigestUpdate(ctx, salt, sizeof(salt)); |
3677 | 124 | EVP_DigestFinal(ctx, d.digest, &len); |
3678 | 124 | EVP_MD_CTX_free(ctx); |
3679 | | |
3680 | 124 | return d.extract; |
3681 | 124 | } |
3682 | | |
3683 | | |
3684 | | /* |
3685 | | * generate_nonce - generate client-address-specific nonce string. |
3686 | | */ |
3687 | | static void generate_nonce( |
3688 | | struct recvbuf * rbufp, |
3689 | | char * nonce, |
3690 | | size_t nonce_octets |
3691 | | ) |
3692 | 37 | { |
3693 | 37 | u_int32 derived; |
3694 | | |
3695 | 37 | derived = derive_nonce(&rbufp->recv_srcadr, |
3696 | 37 | rbufp->recv_time.l_ui, |
3697 | 37 | rbufp->recv_time.l_uf); |
3698 | 37 | snprintf(nonce, nonce_octets, "%08x%08x%08x", |
3699 | 37 | rbufp->recv_time.l_ui, rbufp->recv_time.l_uf, derived); |
3700 | 37 | } |
3701 | | |
3702 | | |
3703 | | /* |
3704 | | * validate_nonce - validate client-address-specific nonce string. |
3705 | | * |
3706 | | * Returns TRUE if the local calculation of the nonce matches the |
3707 | | * client-provided value and the timestamp is recent enough. |
3708 | | */ |
3709 | | static int validate_nonce( |
3710 | | const char * pnonce, |
3711 | | struct recvbuf * rbufp |
3712 | | ) |
3713 | 101 | { |
3714 | 101 | u_int ts_i; |
3715 | 101 | u_int ts_f; |
3716 | 101 | l_fp ts; |
3717 | 101 | l_fp now_delta; |
3718 | 101 | u_int supposed; |
3719 | 101 | u_int derived; |
3720 | | |
3721 | 101 | if (3 != sscanf(pnonce, "%08x%08x%08x", &ts_i, &ts_f, &supposed)) |
3722 | 14 | return FALSE; |
3723 | | |
3724 | 87 | ts.l_ui = (u_int32)ts_i; |
3725 | 87 | ts.l_uf = (u_int32)ts_f; |
3726 | 87 | derived = derive_nonce(&rbufp->recv_srcadr, ts.l_ui, ts.l_uf); |
3727 | 87 | get_systime(&now_delta); |
3728 | 87 | L_SUB(&now_delta, &ts); |
3729 | | |
3730 | 87 | return (supposed == derived && now_delta.l_ui < 16); |
3731 | 101 | } |
3732 | | |
3733 | | |
3734 | | /* |
3735 | | * send_random_tag_value - send a randomly-generated three character |
3736 | | * tag prefix, a '.', an index, a '=' and a |
3737 | | * random integer value. |
3738 | | * |
3739 | | * To try to force clients to ignore unrecognized tags in mrulist, |
3740 | | * reslist, and ifstats responses, the first and last rows are spiced |
3741 | | * with randomly-generated tag names with correct .# index. Make it |
3742 | | * three characters knowing that none of the currently-used subscripted |
3743 | | * tags have that length, avoiding the need to test for |
3744 | | * tag collision. |
3745 | | */ |
3746 | | static void |
3747 | | send_random_tag_value( |
3748 | | int indx |
3749 | | ) |
3750 | 0 | { |
3751 | 0 | int noise; |
3752 | 0 | char buf[32]; |
3753 | |
|
3754 | 0 | noise = rand() ^ (rand() << 16); |
3755 | 0 | buf[0] = 'a' + noise % 26; |
3756 | 0 | noise >>= 5; |
3757 | 0 | buf[1] = 'a' + noise % 26; |
3758 | 0 | noise >>= 5; |
3759 | 0 | buf[2] = 'a' + noise % 26; |
3760 | 0 | noise >>= 5; |
3761 | 0 | buf[3] = '.'; |
3762 | 0 | snprintf(&buf[4], sizeof(buf) - 4, "%d", indx); |
3763 | 0 | ctl_putuint(buf, noise); |
3764 | 0 | } |
3765 | | |
3766 | | |
3767 | | /* |
3768 | | * Send a MRU list entry in response to a "ntpq -c mrulist" operation. |
3769 | | * |
3770 | | * To keep clients honest about not depending on the order of values, |
3771 | | * and thereby avoid being locked into ugly workarounds to maintain |
3772 | | * backward compatibility later as new fields are added to the response, |
3773 | | * the order is random. |
3774 | | */ |
3775 | | static void |
3776 | | send_mru_entry( |
3777 | | mon_entry * mon, |
3778 | | int count |
3779 | | ) |
3780 | 0 | { |
3781 | 0 | const char first_fmt[] = "first.%d"; |
3782 | 0 | const char ct_fmt[] = "ct.%d"; |
3783 | 0 | const char mv_fmt[] = "mv.%d"; |
3784 | 0 | const char rs_fmt[] = "rs.%d"; |
3785 | 0 | char tag[32]; |
3786 | 0 | u_char sent[6]; /* 6 tag=value pairs */ |
3787 | 0 | u_int32 noise; |
3788 | 0 | u_int which; |
3789 | 0 | u_int remaining; |
3790 | 0 | const char * pch; |
3791 | |
|
3792 | 0 | remaining = COUNTOF(sent); |
3793 | 0 | ZERO(sent); |
3794 | 0 | noise = (u_int32)(rand() ^ (rand() << 16)); |
3795 | 0 | while (remaining > 0) { |
3796 | 0 | which = (noise & 7) % COUNTOF(sent); |
3797 | 0 | noise >>= 3; |
3798 | 0 | while (sent[which]) |
3799 | 0 | which = (which + 1) % COUNTOF(sent); |
3800 | |
|
3801 | 0 | switch (which) { |
3802 | | |
3803 | 0 | case 0: |
3804 | 0 | snprintf(tag, sizeof(tag), addr_fmt, count); |
3805 | 0 | pch = sptoa(&mon->rmtadr); |
3806 | 0 | ctl_putunqstr(tag, pch, strlen(pch)); |
3807 | 0 | break; |
3808 | | |
3809 | 0 | case 1: |
3810 | 0 | snprintf(tag, sizeof(tag), last_fmt, count); |
3811 | 0 | ctl_putts(tag, &mon->last); |
3812 | 0 | break; |
3813 | | |
3814 | 0 | case 2: |
3815 | 0 | snprintf(tag, sizeof(tag), first_fmt, count); |
3816 | 0 | ctl_putts(tag, &mon->first); |
3817 | 0 | break; |
3818 | | |
3819 | 0 | case 3: |
3820 | 0 | snprintf(tag, sizeof(tag), ct_fmt, count); |
3821 | 0 | ctl_putint(tag, mon->count); |
3822 | 0 | break; |
3823 | | |
3824 | 0 | case 4: |
3825 | 0 | snprintf(tag, sizeof(tag), mv_fmt, count); |
3826 | 0 | ctl_putuint(tag, mon->vn_mode); |
3827 | 0 | break; |
3828 | | |
3829 | 0 | case 5: |
3830 | 0 | snprintf(tag, sizeof(tag), rs_fmt, count); |
3831 | 0 | ctl_puthex(tag, mon->flags); |
3832 | 0 | break; |
3833 | 0 | } |
3834 | 0 | sent[which] = TRUE; |
3835 | 0 | remaining--; |
3836 | 0 | } |
3837 | 0 | } |
3838 | | |
3839 | | |
3840 | | /* |
3841 | | * read_mru_list - supports ntpq's mrulist command. |
3842 | | * |
3843 | | * The challenge here is to match ntpdc's monlist functionality without |
3844 | | * being limited to hundreds of entries returned total, and without |
3845 | | * requiring state on the server. If state were required, ntpq's |
3846 | | * mrulist command would require authentication. |
3847 | | * |
3848 | | * The approach was suggested by Ry Jones. A finite and variable number |
3849 | | * of entries are retrieved per request, to avoid having responses with |
3850 | | * such large numbers of packets that socket buffers are overflowed and |
3851 | | * packets lost. The entries are retrieved oldest-first, taking into |
3852 | | * account that the MRU list will be changing between each request. We |
3853 | | * can expect to see duplicate entries for addresses updated in the MRU |
3854 | | * list during the fetch operation. In the end, the client can assemble |
3855 | | * a close approximation of the MRU list at the point in time the last |
3856 | | * response was sent by ntpd. The only difference is it may be longer, |
3857 | | * containing some number of oldest entries which have since been |
3858 | | * reclaimed. If necessary, the protocol could be extended to zap those |
3859 | | * from the client snapshot at the end, but so far that doesn't seem |
3860 | | * useful. |
3861 | | * |
3862 | | * To accomodate the changing MRU list, the starting point for requests |
3863 | | * after the first request is supplied as a series of last seen |
3864 | | * timestamps and associated addresses, the newest ones the client has |
3865 | | * received. As long as at least one of those entries hasn't been |
3866 | | * bumped to the head of the MRU list, ntpd can pick up at that point. |
3867 | | * Otherwise, the request is failed and it is up to ntpq to back up and |
3868 | | * provide the next newest entry's timestamps and addresses, conceivably |
3869 | | * backing up all the way to the starting point. |
3870 | | * |
3871 | | * input parameters: |
3872 | | * nonce= Regurgitated nonce retrieved by the client |
3873 | | * previously using CTL_OP_REQ_NONCE, demonstrating |
3874 | | * ability to receive traffic sent to its address. |
3875 | | * frags= Limit on datagrams (fragments) in response. Used |
3876 | | * by newer ntpq versions instead of limit= when |
3877 | | * retrieving multiple entries. |
3878 | | * limit= Limit on MRU entries returned. One of frags= or |
3879 | | * limit= must be provided. |
3880 | | * limit=1 is a special case: Instead of fetching |
3881 | | * beginning with the supplied starting point's |
3882 | | * newer neighbor, fetch the supplied entry, and |
3883 | | * in that case the #.last timestamp can be zero. |
3884 | | * This enables fetching a single entry by IP |
3885 | | * address. When limit is not one and frags= is |
3886 | | * provided, the fragment limit controls. |
3887 | | * mincount= (decimal) Return entries with count >= mincount. |
3888 | | * laddr= Return entries associated with the server's IP |
3889 | | * address given. No port specification is needed, |
3890 | | * and any supplied is ignored. |
3891 | | * resall= 0x-prefixed hex restrict bits which must all be |
3892 | | * lit for an MRU entry to be included. |
3893 | | * Has precedence over any resany=. |
3894 | | * resany= 0x-prefixed hex restrict bits, at least one of |
3895 | | * which must be list for an MRU entry to be |
3896 | | * included. |
3897 | | * last.0= 0x-prefixed hex l_fp timestamp of newest entry |
3898 | | * which client previously received. |
3899 | | * addr.0= text of newest entry's IP address and port, |
3900 | | * IPv6 addresses in bracketed form: [::]:123 |
3901 | | * last.1= timestamp of 2nd newest entry client has. |
3902 | | * addr.1= address of 2nd newest entry. |
3903 | | * [...] |
3904 | | * |
3905 | | * ntpq provides as many last/addr pairs as will fit in a single request |
3906 | | * packet, except for the first request in a MRU fetch operation. |
3907 | | * |
3908 | | * The response begins with a new nonce value to be used for any |
3909 | | * followup request. Following the nonce is the next newer entry than |
3910 | | * referred to by last.0 and addr.0, if the "0" entry has not been |
3911 | | * bumped to the front. If it has, the first entry returned will be the |
3912 | | * next entry newer than referred to by last.1 and addr.1, and so on. |
3913 | | * If none of the referenced entries remain unchanged, the request fails |
3914 | | * and ntpq backs up to the next earlier set of entries to resync. |
3915 | | * |
3916 | | * Except for the first response, the response begins with confirmation |
3917 | | * of the entry that precedes the first additional entry provided: |
3918 | | * |
3919 | | * last.older= hex l_fp timestamp matching one of the input |
3920 | | * .last timestamps, which entry now precedes the |
3921 | | * response 0. entry in the MRU list. |
3922 | | * addr.older= text of address corresponding to older.last. |
3923 | | * |
3924 | | * And in any case, a successful response contains sets of values |
3925 | | * comprising entries, with the oldest numbered 0 and incrementing from |
3926 | | * there: |
3927 | | * |
3928 | | * addr.# text of IPv4 or IPv6 address and port |
3929 | | * last.# hex l_fp timestamp of last receipt |
3930 | | * first.# hex l_fp timestamp of first receipt |
3931 | | * ct.# count of packets received |
3932 | | * mv.# mode and version |
3933 | | * rs.# restriction mask (RES_* bits) |
3934 | | * |
3935 | | * Note the code currently assumes there are no valid three letter |
3936 | | * tags sent with each row, and needs to be adjusted if that changes. |
3937 | | * |
3938 | | * The client should accept the values in any order, and ignore .# |
3939 | | * values which it does not understand, to allow a smooth path to |
3940 | | * future changes without requiring a new opcode. Clients can rely |
3941 | | * on all *.0 values preceding any *.1 values, that is all values for |
3942 | | * a given index number are together in the response. |
3943 | | * |
3944 | | * The end of the response list is noted with one or two tag=value |
3945 | | * pairs. Unconditionally: |
3946 | | * |
3947 | | * now= 0x-prefixed l_fp timestamp at the server marking |
3948 | | * the end of the operation. |
3949 | | * |
3950 | | * If any entries were returned, now= is followed by: |
3951 | | * |
3952 | | * last.newest= hex l_fp identical to last.# of the prior |
3953 | | * entry. |
3954 | | */ |
3955 | | static void read_mru_list( |
3956 | | struct recvbuf *rbufp, |
3957 | | int restrict_mask |
3958 | | ) |
3959 | 866 | { |
3960 | 866 | static const char nulltxt[1] = { '\0' }; |
3961 | 866 | static const char nonce_text[] = "nonce"; |
3962 | 866 | static const char frags_text[] = "frags"; |
3963 | 866 | static const char limit_text[] = "limit"; |
3964 | 866 | static const char mincount_text[] = "mincount"; |
3965 | 866 | static const char resall_text[] = "resall"; |
3966 | 866 | static const char resany_text[] = "resany"; |
3967 | 866 | static const char maxlstint_text[] = "maxlstint"; |
3968 | 866 | static const char laddr_text[] = "laddr"; |
3969 | 866 | static const char resaxx_fmt[] = "0x%hx"; |
3970 | | |
3971 | 866 | u_int limit; |
3972 | 866 | u_short frags; |
3973 | 866 | u_short resall; |
3974 | 866 | u_short resany; |
3975 | 866 | int mincount; |
3976 | 866 | u_int maxlstint; |
3977 | 866 | sockaddr_u laddr; |
3978 | 866 | struct interface * lcladr; |
3979 | 866 | u_int count; |
3980 | 866 | u_int ui; |
3981 | 866 | u_int uf; |
3982 | 866 | l_fp last[16]; |
3983 | 866 | sockaddr_u addr[COUNTOF(last)]; |
3984 | 866 | char buf[128]; |
3985 | 866 | struct ctl_var * in_parms; |
3986 | 866 | const struct ctl_var * v; |
3987 | 866 | const char * val; |
3988 | 866 | const char * pch; |
3989 | 866 | char * pnonce; |
3990 | 866 | int nonce_valid; |
3991 | 866 | size_t i; |
3992 | 866 | int priors; |
3993 | 866 | u_short hash; |
3994 | 866 | mon_entry * mon; |
3995 | 866 | mon_entry * prior_mon; |
3996 | 866 | l_fp now; |
3997 | | |
3998 | 866 | if (RES_NOMRULIST & restrict_mask) { |
3999 | 0 | ctl_error(CERR_PERMISSION); |
4000 | 0 | NLOG(NLOG_SYSINFO) |
4001 | 0 | msyslog(LOG_NOTICE, |
4002 | 0 | "mrulist from %s rejected due to nomrulist restriction", |
4003 | 0 | stoa(&rbufp->recv_srcadr)); |
4004 | 0 | sys_restricted++; |
4005 | 0 | return; |
4006 | 0 | } |
4007 | | /* |
4008 | | * fill in_parms var list with all possible input parameters. |
4009 | | */ |
4010 | 866 | in_parms = NULL; |
4011 | 866 | set_var(&in_parms, nonce_text, sizeof(nonce_text), 0); |
4012 | 866 | set_var(&in_parms, frags_text, sizeof(frags_text), 0); |
4013 | 866 | set_var(&in_parms, limit_text, sizeof(limit_text), 0); |
4014 | 866 | set_var(&in_parms, mincount_text, sizeof(mincount_text), 0); |
4015 | 866 | set_var(&in_parms, resall_text, sizeof(resall_text), 0); |
4016 | 866 | set_var(&in_parms, resany_text, sizeof(resany_text), 0); |
4017 | 866 | set_var(&in_parms, maxlstint_text, sizeof(maxlstint_text), 0); |
4018 | 866 | set_var(&in_parms, laddr_text, sizeof(laddr_text), 0); |
4019 | 14.7k | for (i = 0; i < COUNTOF(last); i++) { |
4020 | 13.8k | snprintf(buf, sizeof(buf), last_fmt, (int)i); |
4021 | 13.8k | set_var(&in_parms, buf, strlen(buf) + 1, 0); |
4022 | 13.8k | snprintf(buf, sizeof(buf), addr_fmt, (int)i); |
4023 | 13.8k | set_var(&in_parms, buf, strlen(buf) + 1, 0); |
4024 | 13.8k | } |
4025 | | |
4026 | | /* decode input parms */ |
4027 | 866 | pnonce = NULL; |
4028 | 866 | frags = 0; |
4029 | 866 | limit = 0; |
4030 | 866 | mincount = 0; |
4031 | 866 | resall = 0; |
4032 | 866 | resany = 0; |
4033 | 866 | maxlstint = 0; |
4034 | 866 | lcladr = NULL; |
4035 | 866 | priors = 0; |
4036 | 866 | ZERO(last); |
4037 | 866 | ZERO(addr); |
4038 | | |
4039 | | /* have to go through '(void*)' to drop 'const' property from pointer. |
4040 | | * ctl_getitem()' needs some cleanup, too.... perlinger@ntp.org |
4041 | | */ |
4042 | 2.22k | while (NULL != (v = ctl_getitem(in_parms, (void*)&val)) && |
4043 | 2.22k | !(EOV & v->flags)) { |
4044 | 2.03k | int si; |
4045 | | |
4046 | 2.03k | if (NULL == val) |
4047 | 277 | val = nulltxt; |
4048 | | |
4049 | 2.03k | if (!strcmp(nonce_text, v->text)) { |
4050 | 519 | free(pnonce); |
4051 | 519 | pnonce = (*val) ? estrdup(val) : NULL; |
4052 | 1.51k | } else if (!strcmp(frags_text, v->text)) { |
4053 | 197 | if (1 != sscanf(val, "%hu", &frags)) |
4054 | 3 | goto blooper; |
4055 | 1.32k | } else if (!strcmp(limit_text, v->text)) { |
4056 | 145 | if (1 != sscanf(val, "%u", &limit)) |
4057 | 2 | goto blooper; |
4058 | 1.17k | } else if (!strcmp(mincount_text, v->text)) { |
4059 | 154 | if (1 != sscanf(val, "%d", &mincount)) |
4060 | 0 | goto blooper; |
4061 | 154 | if (mincount < 0) |
4062 | 78 | mincount = 0; |
4063 | 1.02k | } else if (!strcmp(resall_text, v->text)) { |
4064 | 70 | if (1 != sscanf(val, resaxx_fmt, &resall)) |
4065 | 2 | goto blooper; |
4066 | 952 | } else if (!strcmp(resany_text, v->text)) { |
4067 | 48 | if (1 != sscanf(val, resaxx_fmt, &resany)) |
4068 | 2 | goto blooper; |
4069 | 904 | } else if (!strcmp(maxlstint_text, v->text)) { |
4070 | 77 | if (1 != sscanf(val, "%u", &maxlstint)) |
4071 | 2 | goto blooper; |
4072 | 827 | } else if (!strcmp(laddr_text, v->text)) { |
4073 | 313 | if (!decodenetnum(val, &laddr)) |
4074 | 313 | goto blooper; |
4075 | 0 | lcladr = getinterface(&laddr, 0); |
4076 | 514 | } else if (1 == sscanf(v->text, last_fmt, &si) && |
4077 | 514 | (size_t)si < COUNTOF(last)) { |
4078 | 165 | if (2 != sscanf(val, "0x%08x.%08x", &ui, &uf)) |
4079 | 5 | goto blooper; |
4080 | 160 | last[si].l_ui = ui; |
4081 | 160 | last[si].l_uf = uf; |
4082 | 160 | if (!SOCK_UNSPEC(&addr[si]) && si == priors) |
4083 | 0 | priors++; |
4084 | 349 | } else if (1 == sscanf(v->text, addr_fmt, &si) && |
4085 | 349 | (size_t)si < COUNTOF(addr)) { |
4086 | 349 | if (!decodenetnum(val, &addr[si])) |
4087 | 349 | goto blooper; |
4088 | 0 | if (last[si].l_ui && last[si].l_uf && si == priors) |
4089 | 0 | priors++; |
4090 | 0 | } else { |
4091 | 0 | DPRINTF(1, ("read_mru_list: invalid key item: '%s' (ignored)\n", |
4092 | 0 | v->text)); |
4093 | 0 | continue; |
4094 | | |
4095 | 678 | blooper: |
4096 | 678 | DPRINTF(1, ("read_mru_list: invalid param for '%s': '%s' (bailing)\n", |
4097 | 678 | v->text, val)); |
4098 | 678 | free(pnonce); |
4099 | 678 | pnonce = NULL; |
4100 | 678 | break; |
4101 | 0 | } |
4102 | 2.03k | } |
4103 | 866 | free_varlist(in_parms); |
4104 | 866 | in_parms = NULL; |
4105 | | |
4106 | | /* return no responses until the nonce is validated */ |
4107 | 866 | if (NULL == pnonce) |
4108 | 765 | return; |
4109 | | |
4110 | 101 | nonce_valid = validate_nonce(pnonce, rbufp); |
4111 | 101 | free(pnonce); |
4112 | 101 | if (!nonce_valid) |
4113 | 101 | return; |
4114 | | |
4115 | 0 | if ((0 == frags && !(0 < limit && limit <= MRU_ROW_LIMIT)) || |
4116 | 0 | frags > MRU_FRAGS_LIMIT) { |
4117 | 0 | ctl_error(CERR_BADVALUE); |
4118 | 0 | return; |
4119 | 0 | } |
4120 | | |
4121 | | /* |
4122 | | * If either frags or limit is not given, use the max. |
4123 | | */ |
4124 | 0 | if (0 != frags && 0 == limit) |
4125 | 0 | limit = UINT_MAX; |
4126 | 0 | else if (0 != limit && 0 == frags) |
4127 | 0 | frags = MRU_FRAGS_LIMIT; |
4128 | | |
4129 | | /* |
4130 | | * Find the starting point if one was provided. |
4131 | | */ |
4132 | 0 | mon = NULL; |
4133 | 0 | for (i = 0; i < (size_t)priors; i++) { |
4134 | 0 | hash = MON_HASH(&addr[i]); |
4135 | 0 | for (mon = mon_hash[hash]; |
4136 | 0 | mon != NULL; |
4137 | 0 | mon = mon->hash_next) |
4138 | 0 | if (ADDR_PORT_EQ(&mon->rmtadr, &addr[i])) |
4139 | 0 | break; |
4140 | 0 | if (mon != NULL) { |
4141 | 0 | if (L_ISEQU(&mon->last, &last[i])) |
4142 | 0 | break; |
4143 | 0 | mon = NULL; |
4144 | 0 | } |
4145 | 0 | } |
4146 | | |
4147 | | /* If a starting point was provided... */ |
4148 | 0 | if (priors) { |
4149 | | /* and none could be found unmodified... */ |
4150 | 0 | if (NULL == mon) { |
4151 | | /* tell ntpq to try again with older entries */ |
4152 | 0 | ctl_error(CERR_UNKNOWNVAR); |
4153 | 0 | return; |
4154 | 0 | } |
4155 | | /* confirm the prior entry used as starting point */ |
4156 | 0 | ctl_putts("last.older", &mon->last); |
4157 | 0 | pch = sptoa(&mon->rmtadr); |
4158 | 0 | ctl_putunqstr("addr.older", pch, strlen(pch)); |
4159 | | |
4160 | | /* |
4161 | | * Move on to the first entry the client doesn't have, |
4162 | | * except in the special case of a limit of one. In |
4163 | | * that case return the starting point entry. |
4164 | | */ |
4165 | 0 | if (limit > 1) |
4166 | 0 | mon = PREV_DLIST(mon_mru_list, mon, mru); |
4167 | 0 | } else { /* start with the oldest */ |
4168 | 0 | mon = TAIL_DLIST(mon_mru_list, mru); |
4169 | 0 | } |
4170 | | |
4171 | | /* |
4172 | | * send up to limit= entries in up to frags= datagrams |
4173 | | */ |
4174 | 0 | get_systime(&now); |
4175 | 0 | generate_nonce(rbufp, buf, sizeof(buf)); |
4176 | 0 | ctl_putunqstr("nonce", buf, strlen(buf)); |
4177 | 0 | prior_mon = NULL; |
4178 | 0 | for (count = 0; |
4179 | 0 | mon != NULL && res_frags < frags && count < limit; |
4180 | 0 | mon = PREV_DLIST(mon_mru_list, mon, mru)) { |
4181 | |
|
4182 | 0 | if (mon->count < mincount) |
4183 | 0 | continue; |
4184 | 0 | if (resall && resall != (resall & mon->flags)) |
4185 | 0 | continue; |
4186 | 0 | if (resany && !(resany & mon->flags)) |
4187 | 0 | continue; |
4188 | 0 | if (maxlstint > 0 && now.l_ui - mon->last.l_ui > |
4189 | 0 | maxlstint) |
4190 | 0 | continue; |
4191 | 0 | if (lcladr != NULL && mon->lcladr != lcladr) |
4192 | 0 | continue; |
4193 | | |
4194 | 0 | send_mru_entry(mon, count); |
4195 | 0 | if (!count) |
4196 | 0 | send_random_tag_value(0); |
4197 | 0 | count++; |
4198 | 0 | prior_mon = mon; |
4199 | 0 | } |
4200 | | |
4201 | | /* |
4202 | | * If this batch completes the MRU list, say so explicitly with |
4203 | | * a now= l_fp timestamp. |
4204 | | */ |
4205 | 0 | if (NULL == mon) { |
4206 | 0 | if (count > 1) |
4207 | 0 | send_random_tag_value(count - 1); |
4208 | 0 | ctl_putts("now", &now); |
4209 | | /* if any entries were returned confirm the last */ |
4210 | 0 | if (prior_mon != NULL) |
4211 | 0 | ctl_putts("last.newest", &prior_mon->last); |
4212 | 0 | } |
4213 | 0 | ctl_flushpkt(0); |
4214 | 0 | } |
4215 | | |
4216 | | |
4217 | | /* |
4218 | | * Send a ifstats entry in response to a "ntpq -c ifstats" request. |
4219 | | * |
4220 | | * To keep clients honest about not depending on the order of values, |
4221 | | * and thereby avoid being locked into ugly workarounds to maintain |
4222 | | * backward compatibility later as new fields are added to the response, |
4223 | | * the order is random. |
4224 | | */ |
4225 | | static void |
4226 | | send_ifstats_entry( |
4227 | | endpt * la, |
4228 | | u_int ifnum |
4229 | | ) |
4230 | 0 | { |
4231 | 0 | const char addr_fmtu[] = "addr.%u"; |
4232 | 0 | const char bcast_fmt[] = "bcast.%u"; |
4233 | 0 | const char en_fmt[] = "en.%u"; /* enabled */ |
4234 | 0 | const char name_fmt[] = "name.%u"; |
4235 | 0 | const char flags_fmt[] = "flags.%u"; |
4236 | 0 | const char tl_fmt[] = "tl.%u"; /* ttl */ |
4237 | 0 | const char mc_fmt[] = "mc.%u"; /* mcast count */ |
4238 | 0 | const char rx_fmt[] = "rx.%u"; |
4239 | 0 | const char tx_fmt[] = "tx.%u"; |
4240 | 0 | const char txerr_fmt[] = "txerr.%u"; |
4241 | 0 | const char pc_fmt[] = "pc.%u"; /* peer count */ |
4242 | 0 | const char up_fmt[] = "up.%u"; /* uptime */ |
4243 | 0 | char tag[32]; |
4244 | 0 | u_char sent[IFSTATS_FIELDS]; /* 12 tag=value pairs */ |
4245 | 0 | int noisebits; |
4246 | 0 | u_int32 noise; |
4247 | 0 | u_int which; |
4248 | 0 | u_int remaining; |
4249 | 0 | const char *pch; |
4250 | |
|
4251 | 0 | remaining = COUNTOF(sent); |
4252 | 0 | ZERO(sent); |
4253 | 0 | noise = 0; |
4254 | 0 | noisebits = 0; |
4255 | 0 | while (remaining > 0) { |
4256 | 0 | if (noisebits < 4) { |
4257 | 0 | noise = rand() ^ (rand() << 16); |
4258 | 0 | noisebits = 31; |
4259 | 0 | } |
4260 | 0 | which = (noise & 0xf) % COUNTOF(sent); |
4261 | 0 | noise >>= 4; |
4262 | 0 | noisebits -= 4; |
4263 | |
|
4264 | 0 | while (sent[which]) |
4265 | 0 | which = (which + 1) % COUNTOF(sent); |
4266 | |
|
4267 | 0 | switch (which) { |
4268 | | |
4269 | 0 | case 0: |
4270 | 0 | snprintf(tag, sizeof(tag), addr_fmtu, ifnum); |
4271 | 0 | pch = sptoa(&la->sin); |
4272 | 0 | ctl_putunqstr(tag, pch, strlen(pch)); |
4273 | 0 | break; |
4274 | | |
4275 | 0 | case 1: |
4276 | 0 | snprintf(tag, sizeof(tag), bcast_fmt, ifnum); |
4277 | 0 | if (INT_BCASTOPEN & la->flags) |
4278 | 0 | pch = sptoa(&la->bcast); |
4279 | 0 | else |
4280 | 0 | pch = ""; |
4281 | 0 | ctl_putunqstr(tag, pch, strlen(pch)); |
4282 | 0 | break; |
4283 | | |
4284 | 0 | case 2: |
4285 | 0 | snprintf(tag, sizeof(tag), en_fmt, ifnum); |
4286 | 0 | ctl_putint(tag, !la->ignore_packets); |
4287 | 0 | break; |
4288 | | |
4289 | 0 | case 3: |
4290 | 0 | snprintf(tag, sizeof(tag), name_fmt, ifnum); |
4291 | 0 | ctl_putstr(tag, la->name, strlen(la->name)); |
4292 | 0 | break; |
4293 | | |
4294 | 0 | case 4: |
4295 | 0 | snprintf(tag, sizeof(tag), flags_fmt, ifnum); |
4296 | 0 | ctl_puthex(tag, (u_int)la->flags); |
4297 | 0 | break; |
4298 | | |
4299 | 0 | case 5: |
4300 | 0 | snprintf(tag, sizeof(tag), tl_fmt, ifnum); |
4301 | 0 | ctl_putint(tag, la->last_ttl); |
4302 | 0 | break; |
4303 | | |
4304 | 0 | case 6: |
4305 | 0 | snprintf(tag, sizeof(tag), mc_fmt, ifnum); |
4306 | 0 | ctl_putint(tag, la->num_mcast); |
4307 | 0 | break; |
4308 | | |
4309 | 0 | case 7: |
4310 | 0 | snprintf(tag, sizeof(tag), rx_fmt, ifnum); |
4311 | 0 | ctl_putint(tag, la->received); |
4312 | 0 | break; |
4313 | | |
4314 | 0 | case 8: |
4315 | 0 | snprintf(tag, sizeof(tag), tx_fmt, ifnum); |
4316 | 0 | ctl_putint(tag, la->sent); |
4317 | 0 | break; |
4318 | | |
4319 | 0 | case 9: |
4320 | 0 | snprintf(tag, sizeof(tag), txerr_fmt, ifnum); |
4321 | 0 | ctl_putint(tag, la->notsent); |
4322 | 0 | break; |
4323 | | |
4324 | 0 | case 10: |
4325 | 0 | snprintf(tag, sizeof(tag), pc_fmt, ifnum); |
4326 | 0 | ctl_putuint(tag, la->peercnt); |
4327 | 0 | break; |
4328 | | |
4329 | 0 | case 11: |
4330 | 0 | snprintf(tag, sizeof(tag), up_fmt, ifnum); |
4331 | 0 | ctl_putuint(tag, current_time - la->starttime); |
4332 | 0 | break; |
4333 | 0 | } |
4334 | 0 | sent[which] = TRUE; |
4335 | 0 | remaining--; |
4336 | 0 | } |
4337 | 0 | send_random_tag_value((int)ifnum); |
4338 | 0 | } |
4339 | | |
4340 | | |
4341 | | /* |
4342 | | * read_ifstats - send statistics for each local address, exposed by |
4343 | | * ntpq -c ifstats |
4344 | | */ |
4345 | | static void |
4346 | | read_ifstats( |
4347 | | struct recvbuf * rbufp |
4348 | | ) |
4349 | 0 | { |
4350 | 0 | u_int ifidx; |
4351 | 0 | endpt * la; |
4352 | | |
4353 | | /* |
4354 | | * loop over [0..sys_ifnum] searching ep_list for each |
4355 | | * ifnum in turn. |
4356 | | */ |
4357 | 0 | for (ifidx = 0; ifidx < sys_ifnum; ifidx++) { |
4358 | 0 | for (la = ep_list; la != NULL; la = la->elink) |
4359 | 0 | if (ifidx == la->ifnum) |
4360 | 0 | break; |
4361 | 0 | if (NULL == la) |
4362 | 0 | continue; |
4363 | | /* return stats for one local address */ |
4364 | 0 | send_ifstats_entry(la, ifidx); |
4365 | 0 | } |
4366 | 0 | ctl_flushpkt(0); |
4367 | 0 | } |
4368 | | |
4369 | | static void |
4370 | | sockaddrs_from_restrict_u( |
4371 | | sockaddr_u * psaA, |
4372 | | sockaddr_u * psaM, |
4373 | | restrict_u * pres, |
4374 | | int ipv6 |
4375 | | ) |
4376 | 0 | { |
4377 | 0 | ZERO(*psaA); |
4378 | 0 | ZERO(*psaM); |
4379 | 0 | if (!ipv6) { |
4380 | 0 | psaA->sa.sa_family = AF_INET; |
4381 | 0 | psaA->sa4.sin_addr.s_addr = htonl(pres->u.v4.addr); |
4382 | 0 | psaM->sa.sa_family = AF_INET; |
4383 | 0 | psaM->sa4.sin_addr.s_addr = htonl(pres->u.v4.mask); |
4384 | 0 | } else { |
4385 | 0 | psaA->sa.sa_family = AF_INET6; |
4386 | 0 | memcpy(&psaA->sa6.sin6_addr, &pres->u.v6.addr, |
4387 | 0 | sizeof(psaA->sa6.sin6_addr)); |
4388 | 0 | psaM->sa.sa_family = AF_INET6; |
4389 | 0 | memcpy(&psaM->sa6.sin6_addr, &pres->u.v6.mask, |
4390 | 0 | sizeof(psaA->sa6.sin6_addr)); |
4391 | 0 | } |
4392 | 0 | } |
4393 | | |
4394 | | |
4395 | | /* |
4396 | | * Send a restrict entry in response to a "ntpq -c reslist" request. |
4397 | | * |
4398 | | * To keep clients honest about not depending on the order of values, |
4399 | | * and thereby avoid being locked into ugly workarounds to maintain |
4400 | | * backward compatibility later as new fields are added to the response, |
4401 | | * the order is random. |
4402 | | */ |
4403 | | static void |
4404 | | send_restrict_entry( |
4405 | | restrict_u * pres, |
4406 | | int ipv6, |
4407 | | u_int idx |
4408 | | ) |
4409 | 0 | { |
4410 | 0 | const char addr_fmtu[] = "addr.%u"; |
4411 | 0 | const char mask_fmtu[] = "mask.%u"; |
4412 | 0 | const char hits_fmt[] = "hits.%u"; |
4413 | 0 | const char flags_fmt[] = "flags.%u"; |
4414 | 0 | char tag[32]; |
4415 | 0 | u_char sent[RESLIST_FIELDS]; /* 4 tag=value pairs */ |
4416 | 0 | int noisebits; |
4417 | 0 | u_int32 noise; |
4418 | 0 | u_int which; |
4419 | 0 | u_int remaining; |
4420 | 0 | sockaddr_u addr; |
4421 | 0 | sockaddr_u mask; |
4422 | 0 | const char * pch; |
4423 | 0 | char * buf; |
4424 | 0 | const char * match_str; |
4425 | 0 | const char * access_str; |
4426 | |
|
4427 | 0 | sockaddrs_from_restrict_u(&addr, &mask, pres, ipv6); |
4428 | 0 | remaining = COUNTOF(sent); |
4429 | 0 | ZERO(sent); |
4430 | 0 | noise = 0; |
4431 | 0 | noisebits = 0; |
4432 | 0 | while (remaining > 0) { |
4433 | 0 | if (noisebits < 2) { |
4434 | 0 | noise = rand() ^ (rand() << 16); |
4435 | 0 | noisebits = 31; |
4436 | 0 | } |
4437 | 0 | which = (noise & 0x3) % COUNTOF(sent); |
4438 | 0 | noise >>= 2; |
4439 | 0 | noisebits -= 2; |
4440 | |
|
4441 | 0 | while (sent[which]) |
4442 | 0 | which = (which + 1) % COUNTOF(sent); |
4443 | | |
4444 | | /* XXX: Numbers? Really? */ |
4445 | 0 | switch (which) { |
4446 | | |
4447 | 0 | case 0: |
4448 | 0 | snprintf(tag, sizeof(tag), addr_fmtu, idx); |
4449 | 0 | pch = stoa(&addr); |
4450 | 0 | ctl_putunqstr(tag, pch, strlen(pch)); |
4451 | 0 | break; |
4452 | | |
4453 | 0 | case 1: |
4454 | 0 | snprintf(tag, sizeof(tag), mask_fmtu, idx); |
4455 | 0 | pch = stoa(&mask); |
4456 | 0 | ctl_putunqstr(tag, pch, strlen(pch)); |
4457 | 0 | break; |
4458 | | |
4459 | 0 | case 2: |
4460 | 0 | snprintf(tag, sizeof(tag), hits_fmt, idx); |
4461 | 0 | ctl_putuint(tag, pres->count); |
4462 | 0 | break; |
4463 | | |
4464 | 0 | case 3: |
4465 | 0 | snprintf(tag, sizeof(tag), flags_fmt, idx); |
4466 | 0 | match_str = res_match_flags(pres->mflags); |
4467 | 0 | access_str = res_access_flags(pres->rflags); |
4468 | 0 | if ('\0' == match_str[0]) { |
4469 | 0 | pch = access_str; |
4470 | 0 | } else { |
4471 | 0 | LIB_GETBUF(buf); |
4472 | 0 | snprintf(buf, LIB_BUFLENGTH, "%s %s", |
4473 | 0 | match_str, access_str); |
4474 | 0 | pch = buf; |
4475 | 0 | } |
4476 | 0 | ctl_putunqstr(tag, pch, strlen(pch)); |
4477 | 0 | break; |
4478 | 0 | } |
4479 | 0 | sent[which] = TRUE; |
4480 | 0 | remaining--; |
4481 | 0 | } |
4482 | 0 | send_random_tag_value((int)idx); |
4483 | 0 | } |
4484 | | |
4485 | | |
4486 | | static void |
4487 | | send_restrict_list( |
4488 | | restrict_u * pres, |
4489 | | int ipv6, |
4490 | | u_int * pidx |
4491 | | ) |
4492 | 0 | { |
4493 | 0 | for ( ; pres != NULL; pres = pres->link) { |
4494 | 0 | send_restrict_entry(pres, ipv6, *pidx); |
4495 | 0 | (*pidx)++; |
4496 | 0 | } |
4497 | 0 | } |
4498 | | |
4499 | | |
4500 | | /* |
4501 | | * read_addr_restrictions - returns IPv4 and IPv6 access control lists |
4502 | | */ |
4503 | | static void |
4504 | | read_addr_restrictions( |
4505 | | struct recvbuf * rbufp |
4506 | | ) |
4507 | 0 | { |
4508 | 0 | u_int idx; |
4509 | |
|
4510 | 0 | idx = 0; |
4511 | 0 | send_restrict_list(restrictlist4, FALSE, &idx); |
4512 | 0 | send_restrict_list(restrictlist6, TRUE, &idx); |
4513 | 0 | ctl_flushpkt(0); |
4514 | 0 | } |
4515 | | |
4516 | | |
4517 | | /* |
4518 | | * read_ordlist - CTL_OP_READ_ORDLIST_A for ntpq -c ifstats & reslist |
4519 | | */ |
4520 | | static void |
4521 | | read_ordlist( |
4522 | | struct recvbuf * rbufp, |
4523 | | int restrict_mask |
4524 | | ) |
4525 | 0 | { |
4526 | 0 | const char ifstats_s[] = "ifstats"; |
4527 | 0 | const size_t ifstats_chars = COUNTOF(ifstats_s) - 1; |
4528 | 0 | const char addr_rst_s[] = "addr_restrictions"; |
4529 | 0 | const size_t a_r_chars = COUNTOF(addr_rst_s) - 1; |
4530 | 0 | struct ntp_control * cpkt; |
4531 | 0 | u_short qdata_octets; |
4532 | | |
4533 | | /* |
4534 | | * CTL_OP_READ_ORDLIST_A was first named CTL_OP_READ_IFSTATS and |
4535 | | * used only for ntpq -c ifstats. With the addition of reslist |
4536 | | * the same opcode was generalized to retrieve ordered lists |
4537 | | * which require authentication. The request data is empty or |
4538 | | * contains "ifstats" (not null terminated) to retrieve local |
4539 | | * addresses and associated stats. It is "addr_restrictions" |
4540 | | * to retrieve the IPv4 then IPv6 remote address restrictions, |
4541 | | * which are access control lists. Other request data return |
4542 | | * CERR_UNKNOWNVAR. |
4543 | | */ |
4544 | 0 | cpkt = (struct ntp_control *)&rbufp->recv_pkt; |
4545 | 0 | qdata_octets = ntohs(cpkt->count); |
4546 | 0 | if (0 == qdata_octets || (ifstats_chars == qdata_octets && |
4547 | 0 | !memcmp(ifstats_s, cpkt->u.data, ifstats_chars))) { |
4548 | 0 | read_ifstats(rbufp); |
4549 | 0 | return; |
4550 | 0 | } |
4551 | 0 | if (a_r_chars == qdata_octets && |
4552 | 0 | !memcmp(addr_rst_s, cpkt->u.data, a_r_chars)) { |
4553 | 0 | read_addr_restrictions(rbufp); |
4554 | 0 | return; |
4555 | 0 | } |
4556 | 0 | ctl_error(CERR_UNKNOWNVAR); |
4557 | 0 | } |
4558 | | |
4559 | | |
4560 | | /* |
4561 | | * req_nonce - CTL_OP_REQ_NONCE for ntpq -c mrulist prerequisite. |
4562 | | */ |
4563 | | static void req_nonce( |
4564 | | struct recvbuf * rbufp, |
4565 | | int restrict_mask |
4566 | | ) |
4567 | 37 | { |
4568 | 37 | char buf[64]; |
4569 | | |
4570 | 37 | generate_nonce(rbufp, buf, sizeof(buf)); |
4571 | 37 | ctl_putunqstr("nonce", buf, strlen(buf)); |
4572 | 37 | ctl_flushpkt(0); |
4573 | 37 | } |
4574 | | |
4575 | | |
4576 | | /* |
4577 | | * read_clockstatus - return clock radio status |
4578 | | */ |
4579 | | /*ARGSUSED*/ |
4580 | | static void |
4581 | | read_clockstatus( |
4582 | | struct recvbuf *rbufp, |
4583 | | int restrict_mask |
4584 | | ) |
4585 | 40 | { |
4586 | | #ifndef REFCLOCK |
4587 | | /* |
4588 | | * If no refclock support, no data to return |
4589 | | */ |
4590 | | ctl_error(CERR_BADASSOC); |
4591 | | #else |
4592 | 40 | const struct ctl_var * v; |
4593 | 40 | int i; |
4594 | 40 | struct peer * peer; |
4595 | 40 | char * valuep; |
4596 | 40 | u_char * wants; |
4597 | 40 | size_t wants_alloc; |
4598 | 40 | int gotvar; |
4599 | 40 | const u_char * cc; |
4600 | 40 | struct ctl_var * kv; |
4601 | 40 | struct refclockstat cs; |
4602 | | |
4603 | 40 | if (res_associd != 0) { |
4604 | 35 | peer = findpeerbyassoc(res_associd); |
4605 | 35 | } else { |
4606 | | /* |
4607 | | * Find a clock for this jerk. If the system peer |
4608 | | * is a clock use it, else search peer_list for one. |
4609 | | */ |
4610 | 5 | if (sys_peer != NULL && (FLAG_REFCLOCK & |
4611 | 0 | sys_peer->flags)) |
4612 | 0 | peer = sys_peer; |
4613 | 5 | else |
4614 | 5 | for (peer = peer_list; |
4615 | 5 | peer != NULL; |
4616 | 5 | peer = peer->p_link) |
4617 | 0 | if (FLAG_REFCLOCK & peer->flags) |
4618 | 0 | break; |
4619 | 5 | } |
4620 | 40 | if (NULL == peer || !(FLAG_REFCLOCK & peer->flags)) { |
4621 | 40 | ctl_error(CERR_BADASSOC); |
4622 | 40 | return; |
4623 | 40 | } |
4624 | | /* |
4625 | | * If we got here we have a peer which is a clock. Get his |
4626 | | * status. |
4627 | | */ |
4628 | 0 | cs.kv_list = NULL; |
4629 | 0 | refclock_control(&peer->srcadr, NULL, &cs); |
4630 | 0 | kv = cs.kv_list; |
4631 | | /* |
4632 | | * Look for variables in the packet. |
4633 | | */ |
4634 | 0 | rpkt.status = htons(ctlclkstatus(&cs)); |
4635 | 0 | wants_alloc = CC_MAXCODE + 1 + count_var(kv); |
4636 | 0 | wants = emalloc_zero(wants_alloc); |
4637 | 0 | gotvar = FALSE; |
4638 | 0 | while (NULL != (v = ctl_getitem(clock_var, &valuep))) { |
4639 | 0 | if (!(EOV & v->flags)) { |
4640 | 0 | wants[v->code] = TRUE; |
4641 | 0 | gotvar = TRUE; |
4642 | 0 | } else { |
4643 | 0 | v = ctl_getitem(kv, &valuep); |
4644 | 0 | if (NULL == v) { |
4645 | 0 | ctl_error(CERR_BADVALUE); |
4646 | 0 | free(wants); |
4647 | 0 | free_varlist(cs.kv_list); |
4648 | 0 | return; |
4649 | 0 | } |
4650 | 0 | if (EOV & v->flags) { |
4651 | 0 | ctl_error(CERR_UNKNOWNVAR); |
4652 | 0 | free(wants); |
4653 | 0 | free_varlist(cs.kv_list); |
4654 | 0 | return; |
4655 | 0 | } |
4656 | 0 | wants[CC_MAXCODE + 1 + v->code] = TRUE; |
4657 | 0 | gotvar = TRUE; |
4658 | 0 | } |
4659 | 0 | } |
4660 | | |
4661 | 0 | if (gotvar) { |
4662 | 0 | for (i = 1; i <= CC_MAXCODE; i++) |
4663 | 0 | if (wants[i]) |
4664 | 0 | ctl_putclock(i, &cs, TRUE); |
4665 | 0 | if (kv != NULL) |
4666 | 0 | for (i = 0; !(EOV & kv[i].flags); i++) |
4667 | 0 | if (wants[i + CC_MAXCODE + 1]) |
4668 | 0 | ctl_putdata(kv[i].text, |
4669 | 0 | strlen(kv[i].text), |
4670 | 0 | FALSE); |
4671 | 0 | } else { |
4672 | 0 | for (cc = def_clock_var; *cc != 0; cc++) |
4673 | 0 | ctl_putclock((int)*cc, &cs, FALSE); |
4674 | 0 | for ( ; kv != NULL && !(EOV & kv->flags); kv++) |
4675 | 0 | if (DEF & kv->flags) |
4676 | 0 | ctl_putdata(kv->text, strlen(kv->text), |
4677 | 0 | FALSE); |
4678 | 0 | } |
4679 | |
|
4680 | 0 | free(wants); |
4681 | 0 | free_varlist(cs.kv_list); |
4682 | |
|
4683 | 0 | ctl_flushpkt(0); |
4684 | 0 | #endif |
4685 | 0 | } |
4686 | | |
4687 | | |
4688 | | /* |
4689 | | * write_clockstatus - we don't do this |
4690 | | */ |
4691 | | /*ARGSUSED*/ |
4692 | | static void |
4693 | | write_clockstatus( |
4694 | | struct recvbuf *rbufp, |
4695 | | int restrict_mask |
4696 | | ) |
4697 | 0 | { |
4698 | 0 | ctl_error(CERR_PERMISSION); |
4699 | 0 | } |
4700 | | |
4701 | | /* |
4702 | | * Trap support from here on down. We send async trap messages when the |
4703 | | * upper levels report trouble. Traps can by set either by control |
4704 | | * messages or by configuration. |
4705 | | */ |
4706 | | /* |
4707 | | * set_trap - set a trap in response to a control message |
4708 | | */ |
4709 | | static void |
4710 | | set_trap( |
4711 | | struct recvbuf *rbufp, |
4712 | | int restrict_mask |
4713 | | ) |
4714 | 0 | { |
4715 | 0 | int traptype; |
4716 | | |
4717 | | /* |
4718 | | * See if this guy is allowed |
4719 | | */ |
4720 | 0 | if (restrict_mask & RES_NOTRAP) { |
4721 | 0 | ctl_error(CERR_PERMISSION); |
4722 | 0 | return; |
4723 | 0 | } |
4724 | | |
4725 | | /* |
4726 | | * Determine his allowed trap type. |
4727 | | */ |
4728 | 0 | traptype = TRAP_TYPE_PRIO; |
4729 | 0 | if (restrict_mask & RES_LPTRAP) |
4730 | 0 | traptype = TRAP_TYPE_NONPRIO; |
4731 | | |
4732 | | /* |
4733 | | * Call ctlsettrap() to do the work. Return |
4734 | | * an error if it can't assign the trap. |
4735 | | */ |
4736 | 0 | if (!ctlsettrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype, |
4737 | 0 | (int)res_version)) |
4738 | 0 | ctl_error(CERR_NORESOURCE); |
4739 | 0 | ctl_flushpkt(0); |
4740 | 0 | } |
4741 | | |
4742 | | |
4743 | | /* |
4744 | | * unset_trap - unset a trap in response to a control message |
4745 | | */ |
4746 | | static void |
4747 | | unset_trap( |
4748 | | struct recvbuf *rbufp, |
4749 | | int restrict_mask |
4750 | | ) |
4751 | 0 | { |
4752 | 0 | int traptype; |
4753 | | |
4754 | | /* |
4755 | | * We don't prevent anyone from removing his own trap unless the |
4756 | | * trap is configured. Note we also must be aware of the |
4757 | | * possibility that restriction flags were changed since this |
4758 | | * guy last set his trap. Set the trap type based on this. |
4759 | | */ |
4760 | 0 | traptype = TRAP_TYPE_PRIO; |
4761 | 0 | if (restrict_mask & RES_LPTRAP) |
4762 | 0 | traptype = TRAP_TYPE_NONPRIO; |
4763 | | |
4764 | | /* |
4765 | | * Call ctlclrtrap() to clear this out. |
4766 | | */ |
4767 | 0 | if (!ctlclrtrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype)) |
4768 | 0 | ctl_error(CERR_BADASSOC); |
4769 | 0 | ctl_flushpkt(0); |
4770 | 0 | } |
4771 | | |
4772 | | |
4773 | | /* |
4774 | | * ctlsettrap - called to set a trap |
4775 | | */ |
4776 | | int |
4777 | | ctlsettrap( |
4778 | | sockaddr_u *raddr, |
4779 | | struct interface *linter, |
4780 | | int traptype, |
4781 | | int version |
4782 | | ) |
4783 | 0 | { |
4784 | 0 | size_t n; |
4785 | 0 | struct ctl_trap *tp; |
4786 | 0 | struct ctl_trap *tptouse; |
4787 | | |
4788 | | /* |
4789 | | * See if we can find this trap. If so, we only need update |
4790 | | * the flags and the time. |
4791 | | */ |
4792 | 0 | if ((tp = ctlfindtrap(raddr, linter)) != NULL) { |
4793 | 0 | switch (traptype) { |
4794 | | |
4795 | 0 | case TRAP_TYPE_CONFIG: |
4796 | 0 | tp->tr_flags = TRAP_INUSE|TRAP_CONFIGURED; |
4797 | 0 | break; |
4798 | | |
4799 | 0 | case TRAP_TYPE_PRIO: |
4800 | 0 | if (tp->tr_flags & TRAP_CONFIGURED) |
4801 | 0 | return (1); /* don't change anything */ |
4802 | 0 | tp->tr_flags = TRAP_INUSE; |
4803 | 0 | break; |
4804 | | |
4805 | 0 | case TRAP_TYPE_NONPRIO: |
4806 | 0 | if (tp->tr_flags & TRAP_CONFIGURED) |
4807 | 0 | return (1); /* don't change anything */ |
4808 | 0 | tp->tr_flags = TRAP_INUSE|TRAP_NONPRIO; |
4809 | 0 | break; |
4810 | 0 | } |
4811 | 0 | tp->tr_settime = current_time; |
4812 | 0 | tp->tr_resets++; |
4813 | 0 | return (1); |
4814 | 0 | } |
4815 | | |
4816 | | /* |
4817 | | * First we heard of this guy. Try to find a trap structure |
4818 | | * for him to use, clearing out lesser priority guys if we |
4819 | | * have to. Clear out anyone who's expired while we're at it. |
4820 | | */ |
4821 | 0 | tptouse = NULL; |
4822 | 0 | for (n = 0; n < COUNTOF(ctl_traps); n++) { |
4823 | 0 | tp = &ctl_traps[n]; |
4824 | 0 | if ((TRAP_INUSE & tp->tr_flags) && |
4825 | 0 | !(TRAP_CONFIGURED & tp->tr_flags) && |
4826 | 0 | ((tp->tr_settime + CTL_TRAPTIME) > current_time)) { |
4827 | 0 | tp->tr_flags = 0; |
4828 | 0 | num_ctl_traps--; |
4829 | 0 | } |
4830 | 0 | if (!(TRAP_INUSE & tp->tr_flags)) { |
4831 | 0 | tptouse = tp; |
4832 | 0 | } else if (!(TRAP_CONFIGURED & tp->tr_flags)) { |
4833 | 0 | switch (traptype) { |
4834 | | |
4835 | 0 | case TRAP_TYPE_CONFIG: |
4836 | 0 | if (tptouse == NULL) { |
4837 | 0 | tptouse = tp; |
4838 | 0 | break; |
4839 | 0 | } |
4840 | 0 | if ((TRAP_NONPRIO & tptouse->tr_flags) && |
4841 | 0 | !(TRAP_NONPRIO & tp->tr_flags)) |
4842 | 0 | break; |
4843 | | |
4844 | 0 | if (!(TRAP_NONPRIO & tptouse->tr_flags) |
4845 | 0 | && (TRAP_NONPRIO & tp->tr_flags)) { |
4846 | 0 | tptouse = tp; |
4847 | 0 | break; |
4848 | 0 | } |
4849 | 0 | if (tptouse->tr_origtime < |
4850 | 0 | tp->tr_origtime) |
4851 | 0 | tptouse = tp; |
4852 | 0 | break; |
4853 | | |
4854 | 0 | case TRAP_TYPE_PRIO: |
4855 | 0 | if ( TRAP_NONPRIO & tp->tr_flags) { |
4856 | 0 | if (tptouse == NULL || |
4857 | 0 | ((TRAP_INUSE & |
4858 | 0 | tptouse->tr_flags) && |
4859 | 0 | tptouse->tr_origtime < |
4860 | 0 | tp->tr_origtime)) |
4861 | 0 | tptouse = tp; |
4862 | 0 | } |
4863 | 0 | break; |
4864 | | |
4865 | 0 | case TRAP_TYPE_NONPRIO: |
4866 | 0 | break; |
4867 | 0 | } |
4868 | 0 | } |
4869 | 0 | } |
4870 | | |
4871 | | /* |
4872 | | * If we don't have room for him return an error. |
4873 | | */ |
4874 | 0 | if (tptouse == NULL) |
4875 | 0 | return (0); |
4876 | | |
4877 | | /* |
4878 | | * Set up this structure for him. |
4879 | | */ |
4880 | 0 | tptouse->tr_settime = tptouse->tr_origtime = current_time; |
4881 | 0 | tptouse->tr_count = tptouse->tr_resets = 0; |
4882 | 0 | tptouse->tr_sequence = 1; |
4883 | 0 | tptouse->tr_addr = *raddr; |
4884 | 0 | tptouse->tr_localaddr = linter; |
4885 | 0 | tptouse->tr_version = (u_char) version; |
4886 | 0 | tptouse->tr_flags = TRAP_INUSE; |
4887 | 0 | if (traptype == TRAP_TYPE_CONFIG) |
4888 | 0 | tptouse->tr_flags |= TRAP_CONFIGURED; |
4889 | 0 | else if (traptype == TRAP_TYPE_NONPRIO) |
4890 | 0 | tptouse->tr_flags |= TRAP_NONPRIO; |
4891 | 0 | num_ctl_traps++; |
4892 | 0 | return (1); |
4893 | 0 | } |
4894 | | |
4895 | | |
4896 | | /* |
4897 | | * ctlclrtrap - called to clear a trap |
4898 | | */ |
4899 | | int |
4900 | | ctlclrtrap( |
4901 | | sockaddr_u *raddr, |
4902 | | struct interface *linter, |
4903 | | int traptype |
4904 | | ) |
4905 | 0 | { |
4906 | 0 | register struct ctl_trap *tp; |
4907 | |
|
4908 | 0 | if ((tp = ctlfindtrap(raddr, linter)) == NULL) |
4909 | 0 | return (0); |
4910 | | |
4911 | 0 | if (tp->tr_flags & TRAP_CONFIGURED |
4912 | 0 | && traptype != TRAP_TYPE_CONFIG) |
4913 | 0 | return (0); |
4914 | | |
4915 | 0 | tp->tr_flags = 0; |
4916 | 0 | num_ctl_traps--; |
4917 | 0 | return (1); |
4918 | 0 | } |
4919 | | |
4920 | | |
4921 | | /* |
4922 | | * ctlfindtrap - find a trap given the remote and local addresses |
4923 | | */ |
4924 | | static struct ctl_trap * |
4925 | | ctlfindtrap( |
4926 | | sockaddr_u *raddr, |
4927 | | struct interface *linter |
4928 | | ) |
4929 | 0 | { |
4930 | 0 | size_t n; |
4931 | |
|
4932 | 0 | for (n = 0; n < COUNTOF(ctl_traps); n++) |
4933 | 0 | if ((ctl_traps[n].tr_flags & TRAP_INUSE) |
4934 | 0 | && ADDR_PORT_EQ(raddr, &ctl_traps[n].tr_addr) |
4935 | 0 | && (linter == ctl_traps[n].tr_localaddr)) |
4936 | 0 | return &ctl_traps[n]; |
4937 | | |
4938 | 0 | return NULL; |
4939 | 0 | } |
4940 | | |
4941 | | |
4942 | | /* |
4943 | | * report_event - report an event to the trappers |
4944 | | */ |
4945 | | void |
4946 | | report_event( |
4947 | | int err, /* error code */ |
4948 | | struct peer *peer, /* peer structure pointer */ |
4949 | | const char *str /* protostats string */ |
4950 | | ) |
4951 | 74 | { |
4952 | 74 | char statstr[NTP_MAXSTRLEN]; |
4953 | 74 | int i; |
4954 | 74 | size_t len; |
4955 | | |
4956 | | /* |
4957 | | * Report the error to the protostats file, system log and |
4958 | | * trappers. |
4959 | | */ |
4960 | 74 | if (peer == NULL) { |
4961 | | |
4962 | | /* |
4963 | | * Discard a system report if the number of reports of |
4964 | | * the same type exceeds the maximum. |
4965 | | */ |
4966 | 74 | if (ctl_sys_last_event != (u_char)err) |
4967 | 1 | ctl_sys_num_events= 0; |
4968 | 74 | if (ctl_sys_num_events >= CTL_SYS_MAXEVENTS) |
4969 | 59 | return; |
4970 | | |
4971 | 15 | ctl_sys_last_event = (u_char)err; |
4972 | 15 | ctl_sys_num_events++; |
4973 | 15 | snprintf(statstr, sizeof(statstr), |
4974 | 15 | "0.0.0.0 %04x %02x %s", |
4975 | 15 | ctlsysstatus(), err, eventstr(err)); |
4976 | 15 | if (str != NULL) { |
4977 | 15 | len = strlen(statstr); |
4978 | 15 | snprintf(statstr + len, sizeof(statstr) - len, |
4979 | 15 | " %s", str); |
4980 | 15 | } |
4981 | 15 | NLOG(NLOG_SYSEVENT) |
4982 | 15 | msyslog(LOG_INFO, "%s", statstr); |
4983 | 15 | } else { |
4984 | | |
4985 | | /* |
4986 | | * Discard a peer report if the number of reports of |
4987 | | * the same type exceeds the maximum for that peer. |
4988 | | */ |
4989 | 0 | const char * src; |
4990 | 0 | u_char errlast; |
4991 | |
|
4992 | 0 | errlast = (u_char)err & ~PEER_EVENT; |
4993 | 0 | if (peer->last_event != errlast) |
4994 | 0 | peer->num_events = 0; |
4995 | 0 | if (peer->num_events >= CTL_PEER_MAXEVENTS) |
4996 | 0 | return; |
4997 | | |
4998 | 0 | peer->last_event = errlast; |
4999 | 0 | peer->num_events++; |
5000 | 0 | if (ISREFCLOCKADR(&peer->srcadr)) |
5001 | 0 | src = refnumtoa(&peer->srcadr); |
5002 | 0 | else |
5003 | 0 | src = stoa(&peer->srcadr); |
5004 | |
|
5005 | 0 | snprintf(statstr, sizeof(statstr), |
5006 | 0 | "%s %04x %02x %s", src, |
5007 | 0 | ctlpeerstatus(peer), err, eventstr(err)); |
5008 | 0 | if (str != NULL) { |
5009 | 0 | len = strlen(statstr); |
5010 | 0 | snprintf(statstr + len, sizeof(statstr) - len, |
5011 | 0 | " %s", str); |
5012 | 0 | } |
5013 | 0 | NLOG(NLOG_PEEREVENT) |
5014 | 0 | msyslog(LOG_INFO, "%s", statstr); |
5015 | 0 | } |
5016 | 15 | record_proto_stats(statstr); |
5017 | 15 | #if DEBUG |
5018 | 15 | if (debug) |
5019 | 0 | printf("event at %lu %s\n", current_time, statstr); |
5020 | 15 | #endif |
5021 | | |
5022 | | /* |
5023 | | * If no trappers, return. |
5024 | | */ |
5025 | 15 | if (num_ctl_traps <= 0) |
5026 | 15 | return; |
5027 | | |
5028 | | /* [Bug 3119] |
5029 | | * Peer Events should be associated with a peer -- hence the |
5030 | | * name. But there are instances where this function is called |
5031 | | * *without* a valid peer. This happens e.g. with an unsolicited |
5032 | | * CryptoNAK, or when a leap second alarm is going off while |
5033 | | * currently without a system peer. |
5034 | | * |
5035 | | * The most sensible approach to this seems to bail out here if |
5036 | | * this happens. Avoiding to call this function would also |
5037 | | * bypass the log reporting in the first part of this function, |
5038 | | * and this is probably not the best of all options. |
5039 | | * -*-perlinger@ntp.org-*- |
5040 | | */ |
5041 | 0 | if ((err & PEER_EVENT) && !peer) |
5042 | 0 | return; |
5043 | | |
5044 | | /* |
5045 | | * Set up the outgoing packet variables |
5046 | | */ |
5047 | 0 | res_opcode = CTL_OP_ASYNCMSG; |
5048 | 0 | res_offset = 0; |
5049 | 0 | res_async = TRUE; |
5050 | 0 | res_authenticate = FALSE; |
5051 | 0 | datapt = rpkt.u.data; |
5052 | 0 | dataend = &rpkt.u.data[CTL_MAX_DATA_LEN]; |
5053 | 0 | if (!(err & PEER_EVENT)) { |
5054 | 0 | rpkt.associd = 0; |
5055 | 0 | rpkt.status = htons(ctlsysstatus()); |
5056 | | |
5057 | | /* Include the core system variables and the list. */ |
5058 | 0 | for (i = 1; i <= CS_VARLIST; i++) |
5059 | 0 | ctl_putsys(i); |
5060 | 0 | } else if (NULL != peer) { /* paranoia -- skip output */ |
5061 | 0 | rpkt.associd = htons(peer->associd); |
5062 | 0 | rpkt.status = htons(ctlpeerstatus(peer)); |
5063 | | |
5064 | | /* Dump it all. Later, maybe less. */ |
5065 | 0 | for (i = 1; i <= CP_MAX_NOAUTOKEY; i++) |
5066 | 0 | ctl_putpeer(i, peer); |
5067 | 0 | # ifdef REFCLOCK |
5068 | | /* |
5069 | | * for clock exception events: add clock variables to |
5070 | | * reflect info on exception |
5071 | | */ |
5072 | 0 | if (err == PEVNT_CLOCK) { |
5073 | 0 | struct refclockstat cs; |
5074 | 0 | struct ctl_var *kv; |
5075 | |
|
5076 | 0 | cs.kv_list = NULL; |
5077 | 0 | refclock_control(&peer->srcadr, NULL, &cs); |
5078 | |
|
5079 | 0 | ctl_puthex("refclockstatus", |
5080 | 0 | ctlclkstatus(&cs)); |
5081 | |
|
5082 | 0 | for (i = 1; i <= CC_MAXCODE; i++) |
5083 | 0 | ctl_putclock(i, &cs, FALSE); |
5084 | 0 | for (kv = cs.kv_list; |
5085 | 0 | kv != NULL && !(EOV & kv->flags); |
5086 | 0 | kv++) |
5087 | 0 | if (DEF & kv->flags) |
5088 | 0 | ctl_putdata(kv->text, |
5089 | 0 | strlen(kv->text), |
5090 | 0 | FALSE); |
5091 | 0 | free_varlist(cs.kv_list); |
5092 | 0 | } |
5093 | 0 | # endif /* REFCLOCK */ |
5094 | 0 | } |
5095 | | |
5096 | | /* |
5097 | | * We're done, return. |
5098 | | */ |
5099 | 0 | ctl_flushpkt(0); |
5100 | 0 | } |
5101 | | |
5102 | | |
5103 | | /* |
5104 | | * mprintf_event - printf-style varargs variant of report_event() |
5105 | | */ |
5106 | | int |
5107 | | mprintf_event( |
5108 | | int evcode, /* event code */ |
5109 | | struct peer * p, /* may be NULL */ |
5110 | | const char * fmt, /* msnprintf format */ |
5111 | | ... |
5112 | | ) |
5113 | 0 | { |
5114 | 0 | va_list ap; |
5115 | 0 | int rc; |
5116 | 0 | char msg[512]; |
5117 | |
|
5118 | 0 | va_start(ap, fmt); |
5119 | 0 | rc = mvsnprintf(msg, sizeof(msg), fmt, ap); |
5120 | 0 | va_end(ap); |
5121 | 0 | report_event(evcode, p, msg); |
5122 | |
|
5123 | 0 | return rc; |
5124 | 0 | } |
5125 | | |
5126 | | |
5127 | | /* |
5128 | | * ctl_clr_stats - clear stat counters |
5129 | | */ |
5130 | | void |
5131 | | ctl_clr_stats(void) |
5132 | 1 | { |
5133 | 1 | ctltimereset = current_time; |
5134 | 1 | numctlreq = 0; |
5135 | 1 | numctlbadpkts = 0; |
5136 | 1 | numctlresponses = 0; |
5137 | 1 | numctlfrags = 0; |
5138 | 1 | numctlerrors = 0; |
5139 | 1 | numctlfrags = 0; |
5140 | 1 | numctltooshort = 0; |
5141 | 1 | numctlinputresp = 0; |
5142 | 1 | numctlinputfrag = 0; |
5143 | 1 | numctlinputerr = 0; |
5144 | 1 | numctlbadoffset = 0; |
5145 | 1 | numctlbadversion = 0; |
5146 | 1 | numctldatatooshort = 0; |
5147 | 1 | numctlbadop = 0; |
5148 | 1 | numasyncmsgs = 0; |
5149 | 1 | } |
5150 | | |
5151 | | static u_short |
5152 | | count_var( |
5153 | | const struct ctl_var *k |
5154 | | ) |
5155 | 34.7k | { |
5156 | 34.7k | u_int c; |
5157 | | |
5158 | 34.7k | if (NULL == k) |
5159 | 998 | return 0; |
5160 | | |
5161 | 33.7k | c = 0; |
5162 | 709k | while (!(EOV & (k++)->flags)) |
5163 | 675k | c++; |
5164 | | |
5165 | 33.7k | ENSURE(c <= USHRT_MAX); |
5166 | 0 | return (u_short)c; |
5167 | 34.7k | } |
5168 | | |
5169 | | |
5170 | | char * |
5171 | | add_var( |
5172 | | struct ctl_var **kv, |
5173 | | u_long size, |
5174 | | u_short def |
5175 | | ) |
5176 | 34.6k | { |
5177 | 34.6k | u_short c; |
5178 | 34.6k | struct ctl_var *k; |
5179 | 34.6k | char * buf; |
5180 | | |
5181 | 34.6k | c = count_var(*kv); |
5182 | 34.6k | *kv = erealloc(*kv, (c + 2) * sizeof(**kv)); |
5183 | 34.6k | k = *kv; |
5184 | 34.6k | buf = emalloc(size); |
5185 | 34.6k | k[c].code = c; |
5186 | 34.6k | k[c].text = buf; |
5187 | 34.6k | k[c].flags = def; |
5188 | 34.6k | k[c + 1].code = 0; |
5189 | 34.6k | k[c + 1].text = NULL; |
5190 | 34.6k | k[c + 1].flags = EOV; |
5191 | | |
5192 | 34.6k | return buf; |
5193 | 34.6k | } |
5194 | | |
5195 | | |
5196 | | void |
5197 | | set_var( |
5198 | | struct ctl_var **kv, |
5199 | | const char *data, |
5200 | | u_long size, |
5201 | | u_short def |
5202 | | ) |
5203 | 34.6k | { |
5204 | 34.6k | struct ctl_var *k; |
5205 | 34.6k | const char *s; |
5206 | 34.6k | const char *t; |
5207 | 34.6k | char *td; |
5208 | | |
5209 | 34.6k | if (NULL == data || !size) |
5210 | 0 | return; |
5211 | | |
5212 | 34.6k | k = *kv; |
5213 | 34.6k | if (k != NULL) { |
5214 | 709k | while (!(EOV & k->flags)) { |
5215 | 675k | if (NULL == k->text) { |
5216 | 0 | td = emalloc(size); |
5217 | 0 | memcpy(td, data, size); |
5218 | 0 | k->text = td; |
5219 | 0 | k->flags = def; |
5220 | 0 | return; |
5221 | 675k | } else { |
5222 | 675k | s = data; |
5223 | 675k | t = k->text; |
5224 | 1.79M | while (*t != '=' && *s == *t) { |
5225 | 1.12M | s++; |
5226 | 1.12M | t++; |
5227 | 1.12M | } |
5228 | 675k | if (*s == *t && ((*t == '=') || !*t)) { |
5229 | 0 | td = erealloc((void *)(intptr_t)k->text, size); |
5230 | 0 | memcpy(td, data, size); |
5231 | 0 | k->text = td; |
5232 | 0 | k->flags = def; |
5233 | 0 | return; |
5234 | 0 | } |
5235 | 675k | } |
5236 | 675k | k++; |
5237 | 675k | } |
5238 | 33.7k | } |
5239 | 34.6k | td = add_var(kv, size, def); |
5240 | 34.6k | memcpy(td, data, size); |
5241 | 34.6k | } |
5242 | | |
5243 | | |
5244 | | void |
5245 | | set_sys_var( |
5246 | | const char *data, |
5247 | | u_long size, |
5248 | | u_short def |
5249 | | ) |
5250 | 0 | { |
5251 | 0 | set_var(&ext_sys_var, data, size, def); |
5252 | 0 | } |
5253 | | |
5254 | | |
5255 | | /* |
5256 | | * get_ext_sys_var() retrieves the value of a user-defined variable or |
5257 | | * NULL if the variable has not been setvar'd. |
5258 | | */ |
5259 | | const char * |
5260 | | get_ext_sys_var(const char *tag) |
5261 | 0 | { |
5262 | 0 | struct ctl_var * v; |
5263 | 0 | size_t c; |
5264 | 0 | const char * val; |
5265 | |
|
5266 | 0 | val = NULL; |
5267 | 0 | c = strlen(tag); |
5268 | 0 | for (v = ext_sys_var; !(EOV & v->flags); v++) { |
5269 | 0 | if (NULL != v->text && !memcmp(tag, v->text, c)) { |
5270 | 0 | if ('=' == v->text[c]) { |
5271 | 0 | val = v->text + c + 1; |
5272 | 0 | break; |
5273 | 0 | } else if ('\0' == v->text[c]) { |
5274 | 0 | val = ""; |
5275 | 0 | break; |
5276 | 0 | } |
5277 | 0 | } |
5278 | 0 | } |
5279 | |
|
5280 | 0 | return val; |
5281 | 0 | } |
5282 | | |
5283 | | |
5284 | | void |
5285 | | free_varlist( |
5286 | | struct ctl_var *kv |
5287 | | ) |
5288 | 866 | { |
5289 | 866 | struct ctl_var *k; |
5290 | 866 | if (kv) { |
5291 | 35.5k | for (k = kv; !(k->flags & EOV); k++) |
5292 | 34.6k | free((void *)(intptr_t)k->text); |
5293 | 866 | free((void *)kv); |
5294 | 866 | } |
5295 | 866 | } |