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