558 lines
12 KiB
C
558 lines
12 KiB
C
/*
|
|
* ntptime_config.c
|
|
*
|
|
* What follows is a simplified version of the config parsing code
|
|
* in ntpd/ntp_config.c. We only parse a subset of the configuration
|
|
* syntax, and don't bother whining about things we don't understand.
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
# include <sys/types.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <signal.h>
|
|
#include <ctype.h>
|
|
#include <sys/time.h>
|
|
|
|
#include "ntp_fp.h"
|
|
#include "ntp.h"
|
|
#include "ntp_io.h"
|
|
#include "ntp_unixtime.h"
|
|
#include "ntp_filegen.h"
|
|
#include "ntpdate.h"
|
|
#include "ntp_syslog.h"
|
|
#include "ntp_stdlib.h"
|
|
|
|
/*
|
|
* These routines are used to read the configuration file at
|
|
* startup time. An entry in the file must fit on a single line.
|
|
* Entries are processed as multiple tokens separated by white space
|
|
* Lines are considered terminated when a '#' is encountered. Blank
|
|
* lines are ignored.
|
|
*/
|
|
|
|
/*
|
|
* Configuration file name
|
|
*/
|
|
#ifndef CONFIG_FILE
|
|
# ifndef SYS_WINNT
|
|
# define CONFIG_FILE "/etc/ntp.conf"
|
|
# else /* SYS_WINNT */
|
|
# define CONFIG_FILE "%windir%\\ntp.conf"
|
|
# define ALT_CONFIG_FILE "%windir%\\ntp.ini"
|
|
# endif /* SYS_WINNT */
|
|
#endif /* not CONFIG_FILE */
|
|
|
|
/*
|
|
*
|
|
* We understand the following configuration entries and defaults.
|
|
*
|
|
* peer [ addr ] [ version 3 ] [ key 0 ] [ minpoll 6 ] [ maxpoll 10 ]
|
|
* server [ addr ] [ version 3 ] [ key 0 ] [ minpoll 6 ] [ maxpoll 10 ]
|
|
* keys file_name
|
|
*/
|
|
|
|
#define CONFIG_UNKNOWN 0
|
|
|
|
#define CONFIG_PEER 1
|
|
#define CONFIG_SERVER 2
|
|
#define CONFIG_KEYS 8
|
|
|
|
#define CONF_MOD_VERSION 1
|
|
#define CONF_MOD_KEY 2
|
|
#define CONF_MOD_MINPOLL 3
|
|
#define CONF_MOD_MAXPOLL 4
|
|
#define CONF_MOD_PREFER 5
|
|
#define CONF_MOD_BURST 6
|
|
#define CONF_MOD_SKEY 7
|
|
#define CONF_MOD_TTL 8
|
|
#define CONF_MOD_MODE 9
|
|
|
|
/*
|
|
* Translation table - keywords to function index
|
|
*/
|
|
struct keyword {
|
|
const char *text;
|
|
int keytype;
|
|
};
|
|
|
|
/*
|
|
* Command keywords
|
|
*/
|
|
static struct keyword keywords[] = {
|
|
{ "peer", CONFIG_PEER },
|
|
{ "server", CONFIG_SERVER },
|
|
{ "keys", CONFIG_KEYS },
|
|
{ "", CONFIG_UNKNOWN }
|
|
};
|
|
|
|
/*
|
|
* "peer", "server", "broadcast" modifier keywords
|
|
*/
|
|
static struct keyword mod_keywords[] = {
|
|
{ "version", CONF_MOD_VERSION },
|
|
{ "key", CONF_MOD_KEY },
|
|
{ "minpoll", CONF_MOD_MINPOLL },
|
|
{ "maxpoll", CONF_MOD_MAXPOLL },
|
|
{ "prefer", CONF_MOD_PREFER },
|
|
{ "burst", CONF_MOD_BURST },
|
|
{ "autokey", CONF_MOD_SKEY },
|
|
{ "mode", CONF_MOD_MODE }, /* reference clocks */
|
|
{ "ttl", CONF_MOD_TTL }, /* NTP peers */
|
|
{ "", CONFIG_UNKNOWN }
|
|
};
|
|
|
|
/*
|
|
* Limits on things
|
|
*/
|
|
#define MAXTOKENS 20 /* 20 tokens on line */
|
|
#define MAXLINE 1024 /* maximum length of line */
|
|
#define MAXFILENAME 128 /* maximum length of a file name (alloca()?) */
|
|
|
|
/*
|
|
* Miscellaneous macros
|
|
*/
|
|
#define STRSAME(s1, s2) (*(s1) == *(s2) && strcmp((s1), (s2)) == 0)
|
|
#define ISEOL(c) ((c) == '#' || (c) == '\n' || (c) == '\0')
|
|
#define ISSPACE(c) ((c) == ' ' || (c) == '\t')
|
|
#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
|
|
|
|
/*
|
|
* Systemwide parameters and flags
|
|
*/
|
|
extern struct server **sys_servers; /* the server list */
|
|
extern int sys_numservers; /* number of servers to poll */
|
|
extern char *key_file;
|
|
|
|
/*
|
|
* Function prototypes
|
|
*/
|
|
static int gettokens P((FILE *, char *, char **, int *));
|
|
static int matchkey P((char *, struct keyword *));
|
|
static int getnetnum P((const char *num, struct sockaddr_in *addr,
|
|
int complain));
|
|
|
|
|
|
/*
|
|
* loadservers - load list of NTP servers from configuration file
|
|
*/
|
|
void
|
|
loadservers(
|
|
char *cfgpath
|
|
)
|
|
{
|
|
register int i;
|
|
int errflg;
|
|
int peerversion;
|
|
int minpoll;
|
|
int maxpoll;
|
|
/* int ttl; */
|
|
int srvcnt;
|
|
/* u_long peerkey; */
|
|
int peerflags;
|
|
struct sockaddr_in peeraddr;
|
|
FILE *fp;
|
|
char line[MAXLINE];
|
|
char *(tokens[MAXTOKENS]);
|
|
int ntokens;
|
|
int tok;
|
|
const char *config_file;
|
|
#ifdef SYS_WINNT
|
|
char *alt_config_file;
|
|
LPTSTR temp;
|
|
char config_file_storage[MAX_PATH];
|
|
char alt_config_file_storage[MAX_PATH];
|
|
#endif /* SYS_WINNT */
|
|
struct server *server, *srvlist;
|
|
|
|
/*
|
|
* Initialize, initialize
|
|
*/
|
|
srvcnt = 0;
|
|
srvlist = 0;
|
|
errflg = 0;
|
|
#ifdef DEBUG
|
|
debug = 0;
|
|
#endif /* DEBUG */
|
|
#ifndef SYS_WINNT
|
|
config_file = cfgpath ? cfgpath : CONFIG_FILE;
|
|
#else
|
|
if (cfgpath) {
|
|
config_file = cfgpath;
|
|
} else {
|
|
temp = CONFIG_FILE;
|
|
if (!ExpandEnvironmentStrings((LPCTSTR)temp, (LPTSTR)config_file_storage, (DWORD)sizeof(config_file_storage))) {
|
|
msyslog(LOG_ERR, "ExpandEnvironmentStrings CONFIG_FILE failed: %m\n");
|
|
exit(1);
|
|
}
|
|
config_file = config_file_storage;
|
|
}
|
|
|
|
temp = ALT_CONFIG_FILE;
|
|
if (!ExpandEnvironmentStrings((LPCTSTR)temp, (LPTSTR)alt_config_file_storage, (DWORD)sizeof(alt_config_file_storage))) {
|
|
msyslog(LOG_ERR, "ExpandEnvironmentStrings ALT_CONFIG_FILE failed: %m\n");
|
|
exit(1);
|
|
}
|
|
alt_config_file = alt_config_file_storage;
|
|
M
|
|
#endif /* SYS_WINNT */
|
|
|
|
if ((fp = fopen(FindConfig(config_file), "r")) == NULL)
|
|
{
|
|
fprintf(stderr, "getconfig: Couldn't open <%s>\n", FindConfig(config_file));
|
|
msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(config_file));
|
|
#ifdef SYS_WINNT
|
|
/* Under WinNT try alternate_config_file name, first NTP.CONF, then NTP.INI */
|
|
|
|
if ((fp = fopen(FindConfig(alt_config_file), "r")) == NULL) {
|
|
|
|
/*
|
|
* Broadcast clients can sometimes run without
|
|
* a configuration file.
|
|
*/
|
|
|
|
fprintf(stderr, "getconfig: Couldn't open <%s>\n", FindConfig(alt_config_file));
|
|
msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(alt_config_file));
|
|
return;
|
|
}
|
|
#else /* not SYS_WINNT */
|
|
return;
|
|
#endif /* not SYS_WINNT */
|
|
}
|
|
|
|
while ((tok = gettokens(fp, line, tokens, &ntokens))
|
|
!= CONFIG_UNKNOWN) {
|
|
switch(tok) {
|
|
case CONFIG_PEER:
|
|
case CONFIG_SERVER:
|
|
|
|
if (ntokens < 2) {
|
|
msyslog(LOG_ERR,
|
|
"No address for %s, line ignored",
|
|
tokens[0]);
|
|
break;
|
|
}
|
|
|
|
if (!getnetnum(tokens[1], &peeraddr, 1)) {
|
|
/* Resolve now, or lose! */
|
|
break;
|
|
} else {
|
|
errflg = 0;
|
|
|
|
/* Shouldn't be able to specify multicast */
|
|
if (IN_CLASSD(ntohl(peeraddr.sin_addr.s_addr))
|
|
|| ISBADADR(&peeraddr)) {
|
|
msyslog(LOG_ERR,
|
|
"attempt to configure invalid address %s",
|
|
ntoa(&peeraddr));
|
|
break;
|
|
}
|
|
}
|
|
|
|
peerversion = NTP_VERSION;
|
|
minpoll = NTP_MINDPOLL;
|
|
maxpoll = NTP_MAXDPOLL;
|
|
/* peerkey = 0; */
|
|
peerflags = 0;
|
|
/* ttl = 0; */
|
|
for (i = 2; i < ntokens; i++)
|
|
switch (matchkey(tokens[i], mod_keywords)) {
|
|
case CONF_MOD_VERSION:
|
|
if (i >= ntokens-1) {
|
|
msyslog(LOG_ERR,
|
|
"peer/server version requires an argument");
|
|
errflg = 1;
|
|
break;
|
|
}
|
|
peerversion = atoi(tokens[++i]);
|
|
if ((u_char)peerversion > NTP_VERSION
|
|
|| (u_char)peerversion < NTP_OLDVERSION) {
|
|
msyslog(LOG_ERR,
|
|
"inappropriate version number %s, line ignored",
|
|
tokens[i]);
|
|
errflg = 1;
|
|
}
|
|
break;
|
|
|
|
case CONF_MOD_KEY:
|
|
if (i >= ntokens-1) {
|
|
msyslog(LOG_ERR,
|
|
"key: argument required");
|
|
errflg = 1;
|
|
break;
|
|
}
|
|
++i;
|
|
/* peerkey = (int)atol(tokens[i]); */
|
|
peerflags |= FLAG_AUTHENABLE;
|
|
break;
|
|
|
|
case CONF_MOD_MINPOLL:
|
|
if (i >= ntokens-1) {
|
|
msyslog(LOG_ERR,
|
|
"minpoll: argument required");
|
|
errflg = 1;
|
|
break;
|
|
}
|
|
minpoll = atoi(tokens[++i]);
|
|
if (minpoll < NTP_MINPOLL)
|
|
minpoll = NTP_MINPOLL;
|
|
break;
|
|
|
|
case CONF_MOD_MAXPOLL:
|
|
if (i >= ntokens-1) {
|
|
msyslog(LOG_ERR,
|
|
"maxpoll: argument required"
|
|
);
|
|
errflg = 1;
|
|
break;
|
|
}
|
|
maxpoll = atoi(tokens[++i]);
|
|
if (maxpoll > NTP_MAXPOLL)
|
|
maxpoll = NTP_MAXPOLL;
|
|
break;
|
|
|
|
case CONF_MOD_PREFER:
|
|
peerflags |= FLAG_PREFER;
|
|
break;
|
|
|
|
case CONF_MOD_BURST:
|
|
peerflags |= FLAG_BURST;
|
|
break;
|
|
|
|
case CONF_MOD_SKEY:
|
|
peerflags |= FLAG_SKEY | FLAG_AUTHENABLE;
|
|
break;
|
|
|
|
case CONF_MOD_TTL:
|
|
if (i >= ntokens-1) {
|
|
msyslog(LOG_ERR,
|
|
"ttl: argument required");
|
|
errflg = 1;
|
|
break;
|
|
}
|
|
++i;
|
|
/* ttl = atoi(tokens[i]); */
|
|
break;
|
|
|
|
case CONF_MOD_MODE:
|
|
if (i >= ntokens-1) {
|
|
msyslog(LOG_ERR,
|
|
"mode: argument required");
|
|
errflg = 1;
|
|
break;
|
|
}
|
|
++i;
|
|
/* ttl = atoi(tokens[i]); */
|
|
break;
|
|
|
|
case CONFIG_UNKNOWN:
|
|
errflg = 1;
|
|
break;
|
|
}
|
|
if (minpoll > maxpoll) {
|
|
msyslog(LOG_ERR, "config error: minpoll > maxpoll");
|
|
errflg = 1;
|
|
}
|
|
if (errflg == 0) {
|
|
server = (struct server *)emalloc(sizeof(struct server));
|
|
memset((char *)server, 0, sizeof(struct server));
|
|
server->srcadr = peeraddr;
|
|
server->version = peerversion;
|
|
server->dispersion = PEER_MAXDISP;
|
|
server->next_server = srvlist;
|
|
srvlist = server;
|
|
srvcnt++;
|
|
}
|
|
break;
|
|
|
|
case CONFIG_KEYS:
|
|
if (ntokens >= 2) {
|
|
key_file = (char *) emalloc(strlen(tokens[1]) + 1);
|
|
strcpy(key_file, tokens[1]);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
(void) fclose(fp);
|
|
|
|
/* build final list */
|
|
sys_numservers = srvcnt;
|
|
sys_servers = (struct server **)
|
|
emalloc(sys_numservers * sizeof(struct server *));
|
|
for(i=0;i<sys_numservers;i++) {
|
|
sys_servers[i] = srvlist;
|
|
srvlist = srvlist->next_server;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* gettokens - read a line and return tokens
|
|
*/
|
|
static int
|
|
gettokens(
|
|
FILE *fp,
|
|
char *line,
|
|
char **tokenlist,
|
|
int *ntokens
|
|
)
|
|
{
|
|
register char *cp;
|
|
register int eol;
|
|
register int ntok;
|
|
register int quoted = 0;
|
|
|
|
/*
|
|
* Find start of first token
|
|
*/
|
|
again:
|
|
while ((cp = fgets(line, MAXLINE, fp)) != NULL) {
|
|
cp = line;
|
|
while (ISSPACE(*cp))
|
|
cp++;
|
|
if (!ISEOL(*cp))
|
|
break;
|
|
}
|
|
if (cp == NULL) {
|
|
*ntokens = 0;
|
|
return CONFIG_UNKNOWN; /* hack. Is recognized as EOF */
|
|
}
|
|
|
|
/*
|
|
* Now separate out the tokens
|
|
*/
|
|
eol = 0;
|
|
ntok = 0;
|
|
while (!eol) {
|
|
tokenlist[ntok++] = cp;
|
|
while (!ISEOL(*cp) && (!ISSPACE(*cp) || quoted))
|
|
quoted ^= (*cp++ == '"');
|
|
|
|
if (ISEOL(*cp)) {
|
|
*cp = '\0';
|
|
eol = 1;
|
|
} else { /* must be space */
|
|
*cp++ = '\0';
|
|
while (ISSPACE(*cp))
|
|
cp++;
|
|
if (ISEOL(*cp))
|
|
eol = 1;
|
|
}
|
|
if (ntok == MAXTOKENS)
|
|
eol = 1;
|
|
}
|
|
|
|
/*
|
|
* Return the match
|
|
*/
|
|
*ntokens = ntok;
|
|
ntok = matchkey(tokenlist[0], keywords);
|
|
if (ntok == CONFIG_UNKNOWN)
|
|
goto again;
|
|
return ntok;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* matchkey - match a keyword to a list
|
|
*/
|
|
static int
|
|
matchkey(
|
|
register char *word,
|
|
register struct keyword *keys
|
|
)
|
|
{
|
|
for (;;) {
|
|
if (keys->keytype == CONFIG_UNKNOWN) {
|
|
return CONFIG_UNKNOWN;
|
|
}
|
|
if (STRSAME(word, keys->text))
|
|
return keys->keytype;
|
|
keys++;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* getnetnum - return a net number (this is crude, but careful)
|
|
*/
|
|
static int
|
|
getnetnum(
|
|
const char *num,
|
|
struct sockaddr_in *addr,
|
|
int complain
|
|
)
|
|
{
|
|
register const char *cp;
|
|
register char *bp;
|
|
register int i;
|
|
register int temp;
|
|
char buf[80]; /* will core dump on really stupid stuff */
|
|
u_int32 netnum;
|
|
|
|
/* XXX ELIMINATE replace with decodenetnum */
|
|
cp = num;
|
|
netnum = 0;
|
|
for (i = 0; i < 4; i++) {
|
|
bp = buf;
|
|
while (isdigit((int)*cp))
|
|
*bp++ = *cp++;
|
|
if (bp == buf)
|
|
break;
|
|
|
|
if (i < 3) {
|
|
if (*cp++ != '.')
|
|
break;
|
|
} else if (*cp != '\0')
|
|
break;
|
|
|
|
*bp = '\0';
|
|
temp = atoi(buf);
|
|
if (temp > 255)
|
|
break;
|
|
netnum <<= 8;
|
|
netnum += temp;
|
|
#ifdef DEBUG
|
|
if (debug > 3)
|
|
printf("getnetnum %s step %d buf %s temp %d netnum %lu\n",
|
|
num, i, buf, temp, (u_long)netnum);
|
|
#endif
|
|
}
|
|
|
|
if (i < 4) {
|
|
if (complain)
|
|
msyslog(LOG_ERR,
|
|
"getnetnum: \"%s\" invalid host number, line ignored",
|
|
num);
|
|
#ifdef DEBUG
|
|
if (debug > 3)
|
|
printf(
|
|
"getnetnum: \"%s\" invalid host number, line ignored\n",
|
|
num);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* make up socket address. Clear it out for neatness.
|
|
*/
|
|
memset((void *)addr, 0, sizeof(struct sockaddr_in));
|
|
addr->sin_family = AF_INET;
|
|
addr->sin_port = htons(NTP_PORT);
|
|
addr->sin_addr.s_addr = htonl(netnum);
|
|
#ifdef DEBUG
|
|
if (debug > 1)
|
|
printf("getnetnum given %s, got %s (%lx)\n",
|
|
num, ntoa(addr), (u_long)netnum);
|
|
#endif
|
|
return 1;
|
|
}
|