ntp_intres.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright (c) 1996 by Sun Microsystems, Inc.
* All Rights Reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* routine callable from xntpd, rather than separate program
* also, key info passed in via a global, so no key file needed.
*/
/*
* xntpres - process configuration entries which require use of the resolver
*
* This is meant to be run by xntpd on the fly. It is not guaranteed
* to work properly if run by hand. This is actually a quick hack to
* stave off violence from people who hate using numbers in the
* configuration file (at least I hope the rest of the daemon is
* better than this). Also might provide some ideas about how one
* might go about autoconfiguring an NTP distribution network.
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <ctype.h>
#include <netdb.h>
#include <signal.h>
#include <errno.h>
#include "ntpd.h"
#include "ntp_select.h"
#include "ntp_io.h"
#include "ntp_request.h"
#include "ntp_stdlib.h"
#include "ntp_syslog.h"
/*
* Each item we are to resolve and configure gets one of these
* structures defined for it.
*/
struct conf_entry {
struct conf_entry *ce_next;
char *ce_name; /* name we are trying to resolve */
};
/*
* confentries is a pointer to the list of configuration entries
* we have left to do.
*/
/*
* We take an interrupt every thirty seconds, at which time we decrement
* config_timer and resolve_timer. The former is set to 2, so we retry
* unsucessful reconfigurations every minute. The latter is set to
* an exponentially increasing value which starts at 2 and increases to
* 32. When this expires we retry failed name resolutions.
*
* We sleep SLEEPTIME seconds before doing anything, to give the server
* time to arrange itself.
*/
#define MINRESOLVE 2
#define MAXRESOLVE 32
#define CONFIG_TIME 2
#define ALARM_TIME 30
#define SLEEPTIME 2
static volatile int config_timer = 0;
static volatile int resolve_timer = 0;
static int resolve_value; /* next value of resolve timer */
/*
* Big hack attack
*/
/*
* Select time out. Set to 2 seconds. The server is on the local machine,
* after all.
*/
#define TIMEOUT_SEC 2
#define TIMEOUT_USEC 0
/*
* Input processing. The data on each line in the configuration file
* is supposed to consist of entries in the following order
*/
#define TOK_HOSTNAME 0
#define TOK_HMODE 1
#define TOK_VERSION 2
#define TOK_MINPOLL 3
#define TOK_MAXPOLL 4
#define TOK_FLAGS 5
#define TOK_TTL 6
#define TOK_KEYID 7
#define NUMTOK 8
#define MAXLINESIZE 512
/*
* File descriptor for ntp request code.
*/
static int sockfd = -1;
/* stuff to be filled in by caller */
char *req_file; /* name of the file with configuration info */
/* end stuff to be filled in */
extern int debug; /* use global debug flag */
#ifndef SYS_WINNT
extern int errno;
#endif /* SYS_WINNT */
static RETSIGTYPE bong P((int));
static void checkparent P((void));
static void removeentry P((struct conf_entry *));
static int findhostaddr P((struct conf_entry *));
static void openntp P((void));
static char * nexttoken P((char **));
static void doconfigure P((int));
/*
* assumes: req_key, req_keyid, conffile valid
* syslog still open
*/
void
{
#ifdef HAVE_SIGSUSPEND
sigemptyset(&set);
#endif /* NTP_POSIX_SOURCE */
#ifdef DEBUG
if (debug)
printf("ntp_intres running");
#endif
/* check out auth stuff */
if (!authhavekey(req_keyid)) {
req_keyid );
exit(1);
}
/*
* Read the configuration info
* {this is bogus, since we are forked, but it is easier
* to keep this code - gdt}
*/
req_file);
exit(1);
}
if (!debug )
/*
* Sleep a little to make sure the server is completely up
*/
/*
* Make a first cut at resolving the bunch
*/
doconfigure(1);
if (confentries == NULL)
exit(0); /* done that quick */
/*
* Here we've got some problem children. Set up the timer
* and wait for it.
*/
#ifndef SYS_WINNT
#endif /* SYS_WINNT */
for (;;) {
if (confentries == NULL)
exit(0);
checkparent();
if (resolve_timer == 0) {
if (resolve_value < MAXRESOLVE)
resolve_value <<= 1;
doconfigure(1);
continue;
} else if (config_timer == 0) {
doconfigure(0);
continue;
}
#ifndef SYS_WINNT
/*
* There is a race in here. Is okay, though, since
* all it does is delay things by 30 seconds.
*/
#ifdef HAVE_SIGSUSPEND
sigsuspend(&set);
#else
sigpause(0);
#endif /* HAVE_SIGSUSPEND */
#else
if (config_timer > 0)
config_timer--;
if (resolve_timer > 0)
#endif /* SYS_WINNT */
}
}
#ifndef SYS_WINNT
/*
* bong - service and reschedule an alarm() interrupt
*/
static RETSIGTYPE
int sig;
{
if (config_timer > 0)
config_timer--;
if (resolve_timer > 0)
}
#endif /* SYS_WINNT */
/*
* checkparent - see if our parent process is still running
*
* No need to worry in the Windows NT environment whether the
* main thread is still running, because if it goes
* down it takes the whole process down with it (in
* which case we won't be running this thread either)
* Turn function into NOP;
*/
static void
{
#if !defined (SYS_WINNT) && !defined (SYS_VXWORKS)
/*
* If our parent (the server) has died we will have been
* inherited by init. If so, exit.
*/
if (getppid() == 1) {
exit(0);
}
#endif /* SYS_WINNT && SYS_VXWORKS*/
}
/*
* removeentry - we are done with an entry, remove it from the list
*/
static void
struct conf_entry *entry;
{
register struct conf_entry *ce;
ce = confentries;
return;
}
return;
}
}
}
/*
* addentry - add an entry to the configuration list
*/
static void
char *name;
int mode;
int version;
int minpoll;
int maxpoll;
int flags;
int ttl;
{
register char *cp;
register struct conf_entry *ce;
int len;
ce->ce_peeraddr = 0;
if (confentries == NULL) {
confentries = ce;
} else {
register struct conf_entry *cep;
/* nothing */;
}
}
/*
* findhostaddr - resolve a host name into an address
*
* The routine sticks the address into the entry's ce_peeraddr if it
* gets one. It returns 1 for "success" and 0 for an uncorrectable
* failure. Note that "success" includes try again errors. You can
* tell that you got a try again since ce_peeraddr will still be zero.
*/
static int
struct conf_entry *entry;
{
checkparent(); /* make sure our guy is still running */
#ifndef NODNS
/*
* If the resolver is in use, see if the failure is
* temporary. If so, return success.
*/
#ifndef SYS_WINNT
extern int h_errno;
#endif /* SYS_WINNT */
return (1);
#endif
return (0);
}
/*
* Use the first address. We don't have any way to
* tell preferences and older gethostbyname() implementations
* only return one.
*/
sizeof(struct in_addr));
return (1);
}
/*
* openntp - open a socket to the ntp server
*/
static void
openntp()
{
struct sockaddr_in saddr;
if (sockfd >= 0)
return;
if (sockfd == -1) {
exit(1);
}
/*
* Make the socket non-blocking. We'll wait with select()
*/
#ifndef SYS_WINNT
#if defined(O_NONBLOCK)
exit(1);
}
#else
#if defined(FNDELAY)
exit(1);
}
#else
# include "Bletch: NEED NON BLOCKING IO"
#endif /* FNDDELAY */
#endif /* O_NONBLOCK */
#else /* SYS_WINNT */
{
int on=1;
}
}
#endif /* SYS_WINNT */
exit(1);
}
}
/*
* request - send a configuration request to the server, wait for a response
*/
static int
{
int n;
#ifdef SYS_WINNT
#endif /* SYS_WINNT */
checkparent(); /* make sure our guy is still running */
if (sockfd < 0)
openntp();
#ifdef SYS_WINNT
#endif /* SYS_WINNT */
/*
* Try to clear out any previously received traffic so it
* doesn't fool us. Note the socket is nonblocking.
*/
0) {
}
/*
* Make up a request packet with the configuration info
*/
get_systime(&ts);
/*
* Done. Send it.
*/
#ifndef SYS_WINNT
if (n < 0) {
return 0; /* maybe should exit */
}
#else
/* In the NT world, documentation seems to indicate that there
* exist _write and _read routines that can be used to so blocking
* I/O on sockets. Problem is these routines require a socket
* handle obtained through the _open_osf_handle C run-time API
* of which there is no explanation in the documentation. We need
* nonblocking write's and read's anyway for our purpose here.
* We're therefore forced to deviate a little bit from the Unix
* model here and use the ReadFile and WriteFile Win32 I/O API's
* on the socket
*/
return 0;
}
if (dwWait == WAIT_FAILED)
return 0;
}
#endif /* SYS_WINNT */
/*
* Wait for a response. A weakness of the mode 7 protocol used
* is that there is no way to associate a response with a
* particular request, i.e. the response to this configuration
* request is indistinguishable from that to any other. I should
* fix this some day. In any event, the time out is fairly
* pessimistic to make sure that if an answer is coming back
* at all, we get it.
*/
for (;;) {
if (n < 0)
{
return 0;
}
else if (n == 0)
{
if(debug)
return 0;
}
#ifndef SYS_WINNT
if (n <= 0) {
if (n < 0) {
return 0;
}
continue;
}
#else /* Overlapped I/O used on non-blocking sockets on Windows NT */
return 0;
}
if (dwWait == WAIT_FAILED) {
return 0;
}
continue;
}
n = NumberOfBytesRead;
#endif /* SYS_WINNT */
/*
* Got one. Check through to make sure it is what
* we expect.
*/
if (n < RESP_HEADER_SIZE) {
n);
continue;
}
#ifdef DEBUG
if (debug > 1)
printf("received non-response packet\n");
#endif
continue;
}
#ifdef DEBUG
if (debug > 1)
printf("received fragmented packet\n");
#endif
continue;
}
#ifdef DEBUG
if (debug > 1)
printf("version (%d) or mode (%d) incorrect\n",
#endif
continue;
}
#ifdef DEBUG
if (debug > 1)
printf("nonzero sequence number (%d)\n",
#endif
continue;
}
#ifdef DEBUG
if (debug > 1)
"implementation (%d) or request (%d) incorrect\n",
#endif
continue;
}
#ifdef DEBUG
if (debug > 1)
"nitems (%d) mbz (%d) or itemsize (%d) nonzero\n",
#endif
continue;
}
switch (n) {
case INFO_OKAY:
/* success */
return 1;
case INFO_ERR_IMPL:
"server reports implementation mismatch!!");
return 0;
case INFO_ERR_REQ:
"server claims configuration request is unknown");
return 0;
case INFO_ERR_FMT:
"server indicates a format error occured(!!)");
return 0;
case INFO_ERR_NODATA:
"server indicates no data available (shouldn't happen)");
return 0;
case INFO_ERR_AUTH:
"server returns a permission denied error");
return 0;
default:
"server returns unknown error code %d", n);
return 0;
}
}
}
/*
* nexttoken - return the next token from a line
*/
static char *
char **lptr;
{
register char *cp;
register char *tstart;
/*
* Skip leading white space
*/
cp++;
/*
* If this is the end of the line, return nothing.
*/
return NULL;
}
/*
* Must be the start of a token. Record the pointer and look
* for the end.
*/
cp++;
/*
* Terminate the token with a \0. If this isn't the end of the
* line, space to the next character.
*/
*cp = '\0';
else
*cp++ = '\0';
return tstart;
}
/*
* readconf - read the configuration information out of the file we
* were passed. Note that since the file is supposed to be
* machine generated, we bail out at the first sign of trouble.
*/
static void
char *name;
{
register int i;
int flags;
char buf[MAXLINESIZE];
char *bp;
for (i = 0; i < NUMTOK; i++) {
"tokenizing error in file `%s', quitting",
name);
exit(1);
}
}
for (i = 1; i < NUMTOK; i++) {
"format error for integer token `%s', file `%s', quitting",
exit(1);
}
}
exit(1);
}
exit(1);
}
exit(1);
}
exit(1);
}
!= 0) {
exit(1);
}
flags = 0;
/*
* This is as good as we can check it. Add it in.
*/
}
}
/*
* doconfigure - attempt to resolve names and configure the server
*/
static void
int dores;
{
register struct conf_entry *ce;
register struct conf_entry *ceremove;
ce = confentries;
if (!findhostaddr(ce)) {
"couldn't resolve `%s', giving up on it",
continue;
}
}
if (ce->ce_peeraddr != 0) {
continue;
}
}
}
}