1/*
2 * ntptime_config.c
3 *
4 * What follows is a simplified version of the config parsing code
5 * in ntpd/ntp_config.c.  We only parse a subset of the configuration
6 * syntax, and don't bother whining about things we don't understand.
7 *
8 */
9
10#ifdef HAVE_CONFIG_H
11# include <config.h>
12#endif
13
14#include "ntp_fp.h"
15#include "ntp.h"
16#include "ntp_io.h"
17#include "ntp_unixtime.h"
18#include "ntp_filegen.h"
19#include "ntpdate.h"
20#include "ntp_syslog.h"
21#include "ntp_stdlib.h"
22
23#include <stdio.h>
24#include <signal.h>
25#include <ctype.h>
26
27/*
28 * These routines are used to read the configuration file at
29 * startup time.  An entry in the file must fit on a single line.
30 * Entries are processed as multiple tokens separated by white space
31 * Lines are considered terminated when a '#' is encountered.  Blank
32 * lines are ignored.
33 */
34
35/*
36 * Configuration file name
37 */
38#ifndef CONFIG_FILE
39# ifndef SYS_WINNT
40#  define	CONFIG_FILE "/etc/ntp.conf"
41# else /* SYS_WINNT */
42#  define	CONFIG_FILE 	"%windir%\\ntp.conf"
43#  define	ALT_CONFIG_FILE "%windir%\\ntp.ini"
44# endif /* SYS_WINNT */
45#endif /* not CONFIG_FILE */
46
47/*
48 *
49 * We understand the following configuration entries and defaults.
50 *
51 * peer [ addr ] [ version 3 ] [ key 0 ] [ minpoll 6 ] [ maxpoll 10 ]
52 * server [ addr ] [ version 3 ] [ key 0 ] [ minpoll 6 ] [ maxpoll 10 ]
53 * keys file_name
54 */
55
56#define CONFIG_UNKNOWN		0
57
58#define CONFIG_PEER 		1
59#define CONFIG_SERVER		2
60#define CONFIG_KEYS		8
61
62#define CONF_MOD_VERSION	1
63#define CONF_MOD_KEY		2
64#define CONF_MOD_MINPOLL	3
65#define CONF_MOD_MAXPOLL	4
66#define CONF_MOD_PREFER 	5
67#define CONF_MOD_BURST		6
68#define CONF_MOD_SKEY		7
69#define CONF_MOD_TTL		8
70#define CONF_MOD_MODE		9
71
72/*
73 * Translation table - keywords to function index
74 */
75struct keyword {
76	const char *text;
77	int keytype;
78};
79
80/*
81 * Command keywords
82 */
83static	struct keyword keywords[] = {
84	{ "peer",       CONFIG_PEER },
85	{ "server",     CONFIG_SERVER },
86	{ "keys",       CONFIG_KEYS },
87	{ "",           CONFIG_UNKNOWN }
88};
89
90/*
91 * "peer", "server", "broadcast" modifier keywords
92 */
93static	struct keyword mod_keywords[] = {
94	{ "version",    CONF_MOD_VERSION },
95	{ "key",    CONF_MOD_KEY },
96	{ "minpoll",    CONF_MOD_MINPOLL },
97	{ "maxpoll",    CONF_MOD_MAXPOLL },
98	{ "prefer", CONF_MOD_PREFER },
99	{ "burst",  CONF_MOD_BURST },
100	{ "autokey",    CONF_MOD_SKEY },
101	{ "mode",   CONF_MOD_MODE },    /* reference clocks */
102	{ "ttl",    CONF_MOD_TTL },     /* NTP peers */
103	{ "",       CONFIG_UNKNOWN }
104};
105
106/*
107 * Limits on things
108 */
109#define MAXTOKENS	20	/* 20 tokens on line */
110#define MAXLINE 	1024	/* maximum length of line */
111#define MAXFILENAME 128 /* maximum length of a file name (alloca()?) */
112
113/*
114 * Miscellaneous macros
115 */
116#define STRSAME(s1, s2) 	(*(s1) == *(s2) && strcmp((s1), (s2)) == 0)
117#define ISEOL(c)		((c) == '#' || (c) == '\n' || (c) == '\0')
118#define ISSPACE(c)		((c) == ' ' || (c) == '\t')
119#define STREQ(a, b) 	(*(a) == *(b) && strcmp((a), (b)) == 0)
120
121/*
122 * Systemwide parameters and flags
123 */
124extern struct server **sys_servers;	/* the server list */
125extern int sys_numservers; 	/* number of servers to poll */
126extern char *key_file;
127
128/*
129 * Function prototypes
130 */
131static	int gettokens	P((FILE *, char *, char **, int *));
132static	int matchkey	P((char *, struct keyword *));
133static	int getnetnum	P((const char *num, struct sockaddr_in *addr,
134			   int complain));
135
136
137/*
138 * loadservers - load list of NTP servers from configuration file
139 */
140void
141loadservers(
142	char *cfgpath
143	)
144{
145	register int i;
146	int errflg;
147	int peerversion;
148	int minpoll;
149	int maxpoll;
150	/* int ttl; */
151	int srvcnt;
152	/* u_long peerkey; */
153	int peerflags;
154	struct sockaddr_in peeraddr;
155	FILE *fp;
156	char line[MAXLINE];
157	char *(tokens[MAXTOKENS]);
158	int ntokens;
159	int tok;
160	const char *config_file;
161#ifdef SYS_WINNT
162	char *alt_config_file;
163	LPTSTR temp;
164	char config_file_storage[MAX_PATH];
165	char alt_config_file_storage[MAX_PATH];
166#endif /* SYS_WINNT */
167	struct server *server, *srvlist;
168
169	/*
170	 * Initialize, initialize
171	 */
172	srvcnt = 0;
173	srvlist = 0;
174	errflg = 0;
175#ifdef DEBUG
176	debug = 0;
177#endif	/* DEBUG */
178#ifndef SYS_WINNT
179	config_file = cfgpath ? cfgpath : CONFIG_FILE;
180#else
181	if (cfgpath) {
182		config_file = cfgpath;
183	} else {
184		temp = CONFIG_FILE;
185		if (!ExpandEnvironmentStrings((LPCTSTR)temp, (LPTSTR)config_file_storage, (DWORD)sizeof(config_file_storage))) {
186			msyslog(LOG_ERR, "ExpandEnvironmentStrings CONFIG_FILE failed: %m\n");
187			exit(1);
188		}
189		config_file = config_file_storage;
190	}
191
192	temp = ALT_CONFIG_FILE;
193	if (!ExpandEnvironmentStrings((LPCTSTR)temp, (LPTSTR)alt_config_file_storage, (DWORD)sizeof(alt_config_file_storage))) {
194		msyslog(LOG_ERR, "ExpandEnvironmentStrings ALT_CONFIG_FILE failed: %m\n");
195		exit(1);
196	}
197	alt_config_file = alt_config_file_storage;
198M
199#endif /* SYS_WINNT */
200
201	if ((fp = fopen(FindConfig(config_file), "r")) == NULL)
202	{
203		fprintf(stderr, "getconfig: Couldn't open <%s>\n", FindConfig(config_file));
204		msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(config_file));
205#ifdef SYS_WINNT
206		/* Under WinNT try alternate_config_file name, first NTP.CONF, then NTP.INI */
207
208		if ((fp = fopen(FindConfig(alt_config_file), "r")) == NULL) {
209
210			/*
211			 * Broadcast clients can sometimes run without
212			 * a configuration file.
213			 */
214
215			fprintf(stderr, "getconfig: Couldn't open <%s>\n", FindConfig(alt_config_file));
216			msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(alt_config_file));
217			return;
218		}
219#else  /* not SYS_WINNT */
220		return;
221#endif /* not SYS_WINNT */
222	}
223
224	while ((tok = gettokens(fp, line, tokens, &ntokens))
225	       != CONFIG_UNKNOWN) {
226		switch(tok) {
227		    case CONFIG_PEER:
228		    case CONFIG_SERVER:
229
230			if (ntokens < 2) {
231				msyslog(LOG_ERR,
232					"No address for %s, line ignored",
233					tokens[0]);
234				break;
235			}
236
237			if (!getnetnum(tokens[1], &peeraddr, 1)) {
238				/* Resolve now, or lose! */
239				break;
240			} else {
241				errflg = 0;
242
243				/* Shouldn't be able to specify multicast */
244				if (IN_CLASSD(ntohl(peeraddr.sin_addr.s_addr))
245				    || ISBADADR(&peeraddr)) {
246					msyslog(LOG_ERR,
247						"attempt to configure invalid address %s",
248						ntoa(&peeraddr));
249					break;
250				}
251			}
252
253			peerversion = NTP_VERSION;
254			minpoll = NTP_MINDPOLL;
255			maxpoll = NTP_MAXDPOLL;
256			/* peerkey = 0; */
257			peerflags = 0;
258			/* ttl = 0; */
259			for (i = 2; i < ntokens; i++)
260			    switch (matchkey(tokens[i], mod_keywords)) {
261				case CONF_MOD_VERSION:
262				    if (i >= ntokens-1) {
263					    msyslog(LOG_ERR,
264						    "peer/server version requires an argument");
265					    errflg = 1;
266					    break;
267				    }
268				    peerversion = atoi(tokens[++i]);
269				    if ((u_char)peerversion > NTP_VERSION
270					|| (u_char)peerversion < NTP_OLDVERSION) {
271					    msyslog(LOG_ERR,
272						    "inappropriate version number %s, line ignored",
273						    tokens[i]);
274					    errflg = 1;
275				    }
276				    break;
277
278				case CONF_MOD_KEY:
279				    if (i >= ntokens-1) {
280					    msyslog(LOG_ERR,
281						    "key: argument required");
282					    errflg = 1;
283					    break;
284				    }
285				    ++i;
286				    /* peerkey = (int)atol(tokens[i]); */
287				    peerflags |= FLAG_AUTHENABLE;
288				    break;
289
290				case CONF_MOD_MINPOLL:
291				    if (i >= ntokens-1) {
292					    msyslog(LOG_ERR,
293						    "minpoll: argument required");
294					    errflg = 1;
295					    break;
296				    }
297				    minpoll = atoi(tokens[++i]);
298				    if (minpoll < NTP_MINPOLL)
299					minpoll = NTP_MINPOLL;
300				    break;
301
302				case CONF_MOD_MAXPOLL:
303				    if (i >= ntokens-1) {
304					    msyslog(LOG_ERR,
305						    "maxpoll: argument required"
306						    );
307					    errflg = 1;
308					    break;
309				    }
310				    maxpoll = atoi(tokens[++i]);
311				    if (maxpoll > NTP_MAXPOLL)
312					maxpoll = NTP_MAXPOLL;
313				    break;
314
315				case CONF_MOD_PREFER:
316				    peerflags |= FLAG_PREFER;
317				    break;
318
319				case CONF_MOD_BURST:
320				    peerflags |= FLAG_BURST;
321				    break;
322
323				case CONF_MOD_SKEY:
324				    peerflags |= FLAG_SKEY | FLAG_AUTHENABLE;
325				    break;
326
327				case CONF_MOD_TTL:
328				    if (i >= ntokens-1) {
329					    msyslog(LOG_ERR,
330						    "ttl: argument required");
331					    errflg = 1;
332					    break;
333				    }
334				    ++i;
335				    /* ttl = atoi(tokens[i]); */
336				    break;
337
338				case CONF_MOD_MODE:
339				    if (i >= ntokens-1) {
340					    msyslog(LOG_ERR,
341						    "mode: argument required");
342					    errflg = 1;
343					    break;
344				    }
345				    ++i;
346				    /* ttl = atoi(tokens[i]); */
347				    break;
348
349				case CONFIG_UNKNOWN:
350				    errflg = 1;
351				    break;
352			    }
353			if (minpoll > maxpoll) {
354				msyslog(LOG_ERR, "config error: minpoll > maxpoll");
355				errflg = 1;
356			}
357			if (errflg == 0) {
358				server = (struct server *)emalloc(sizeof(struct server));
359				memset((char *)server, 0, sizeof(struct server));
360				server->srcadr = peeraddr;
361				server->version = peerversion;
362				server->dispersion = PEER_MAXDISP;
363				server->next_server = srvlist;
364				srvlist = server;
365				srvcnt++;
366			}
367			break;
368
369			case CONFIG_KEYS:
370			if (ntokens >= 2) {
371				key_file = (char *) emalloc(strlen(tokens[1]) + 1);
372				strcpy(key_file, tokens[1]);
373			}
374			break;
375		}
376	}
377	(void) fclose(fp);
378
379	/* build final list */
380	sys_numservers = srvcnt;
381	sys_servers = (struct server **)
382	    emalloc(sys_numservers * sizeof(struct server *));
383	for(i=0;i<sys_numservers;i++) {
384		sys_servers[i] = srvlist;
385		srvlist = srvlist->next_server;
386	}
387}
388
389
390
391/*
392 * gettokens - read a line and return tokens
393 */
394static int
395gettokens(
396	FILE *fp,
397	char *line,
398	char **tokenlist,
399	int *ntokens
400	)
401{
402	register char *cp;
403	register int eol;
404	register int ntok;
405	register int quoted = 0;
406
407	/*
408	 * Find start of first token
409	 */
410	again:
411	while ((cp = fgets(line, MAXLINE, fp)) != NULL) {
412		cp = line;
413		while (ISSPACE(*cp))
414			cp++;
415		if (!ISEOL(*cp))
416			break;
417	}
418	if (cp == NULL) {
419		*ntokens = 0;
420		return CONFIG_UNKNOWN;	/* hack.  Is recognized as EOF */
421	}
422
423	/*
424	 * Now separate out the tokens
425	 */
426	eol = 0;
427	ntok = 0;
428	while (!eol) {
429		tokenlist[ntok++] = cp;
430		while (!ISEOL(*cp) && (!ISSPACE(*cp) || quoted))
431			quoted ^= (*cp++ == '"');
432
433		if (ISEOL(*cp)) {
434			*cp = '\0';
435			eol = 1;
436		} else {		/* must be space */
437			*cp++ = '\0';
438			while (ISSPACE(*cp))
439				cp++;
440			if (ISEOL(*cp))
441				eol = 1;
442		}
443		if (ntok == MAXTOKENS)
444			eol = 1;
445	}
446
447	/*
448	 * Return the match
449	 */
450	*ntokens = ntok;
451	ntok = matchkey(tokenlist[0], keywords);
452	if (ntok == CONFIG_UNKNOWN)
453		goto again;
454	return ntok;
455}
456
457
458
459/*
460 * matchkey - match a keyword to a list
461 */
462static int
463matchkey(
464	register char *word,
465	register struct keyword *keys
466	)
467{
468	for (;;) {
469		if (keys->keytype == CONFIG_UNKNOWN) {
470			return CONFIG_UNKNOWN;
471		}
472		if (STRSAME(word, keys->text))
473			return keys->keytype;
474		keys++;
475	}
476}
477
478
479/*
480 * getnetnum - return a net number (this is crude, but careful)
481 */
482static int
483getnetnum(
484	const char *num,
485	struct sockaddr_in *addr,
486	int complain
487	)
488{
489	register const char *cp;
490	register char *bp;
491	register int i;
492	register int temp;
493	char buf[80];		/* will core dump on really stupid stuff */
494	u_int32 netnum;
495
496	/* XXX ELIMINATE replace with decodenetnum */
497	cp = num;
498	netnum = 0;
499	for (i = 0; i < 4; i++) {
500		bp = buf;
501		while (isdigit((int)*cp))
502			*bp++ = *cp++;
503		if (bp == buf)
504			break;
505
506		if (i < 3) {
507			if (*cp++ != '.')
508				break;
509		} else if (*cp != '\0')
510			break;
511
512		*bp = '\0';
513		temp = atoi(buf);
514		if (temp > 255)
515			break;
516		netnum <<= 8;
517		netnum += temp;
518#ifdef DEBUG
519		if (debug > 3)
520			printf("getnetnum %s step %d buf %s temp %d netnum %lu\n",
521			   num, i, buf, temp, (u_long)netnum);
522#endif
523	}
524
525	if (i < 4) {
526		if (complain)
527			msyslog(LOG_ERR,
528				"getnetnum: \"%s\" invalid host number, line ignored",
529				num);
530#ifdef DEBUG
531		if (debug > 3)
532			printf(
533				"getnetnum: \"%s\" invalid host number, line ignored\n",
534				num);
535#endif
536		return 0;
537	}
538
539	/*
540	 * make up socket address.	Clear it out for neatness.
541	 */
542	memset((void *)addr, 0, sizeof(struct sockaddr_in));
543	addr->sin_family = AF_INET;
544	addr->sin_port = htons(NTP_PORT);
545	addr->sin_addr.s_addr = htonl(netnum);
546#ifdef DEBUG
547	if (debug > 1)
548		printf("getnetnum given %s, got %s (%lx)\n",
549		   num, ntoa(addr), (u_long)netnum);
550#endif
551	return 1;
552}
553