Esempio n. 1
0
/*
 * getresponse - get a (series of) response packet(s) and return the data
 */
static int
getresponse(
	int implcode,
	int reqcode,
	int *ritems,
	int *rsize,
	char **rdata,
	int esize
	)
{
	struct resp_pkt rpkt;
	struct sock_timeval tvo;
	int items;
	int i;
	int size;
	int datasize;
	char *datap;
	char *tmp_data;
	char haveseq[MAXSEQ+1];
	int firstpkt;
	int lastseq;
	int numrecv;
	int seq;
	fd_set fds;
	int n;
	int pad;

	/*
	 * This is pretty tricky.  We may get between 1 and many packets
	 * back in response to the request.  We peel the data out of
	 * each packet and collect it in one long block.  When the last
	 * packet in the sequence is received we'll know how many we
	 * should have had.  Note we use one long time out, should reconsider.
	 */
	*ritems = 0;
	*rsize = 0;
	firstpkt = 1;
	numrecv = 0;
	*rdata = datap = pktdata;
	lastseq = 999;	/* too big to be a sequence number */
	memset(haveseq, 0, sizeof(haveseq));
	FD_ZERO(&fds);

    again:
	if (firstpkt)
		tvo = tvout;
	else
		tvo = tvsout;
	
	FD_SET(sockfd, &fds);
	n = select(sockfd+1, &fds, (fd_set *)0, (fd_set *)0, &tvo);

	if (n == -1) {
		warning("select fails", "", "");
		return -1;
	}
	if (n == 0) {
		/*
		 * Timed out.  Return what we have
		 */
		if (firstpkt) {
			(void) fprintf(stderr,
				       "%s: timed out, nothing received\n", currenthost);
			return ERR_TIMEOUT;
		} else {
			(void) fprintf(stderr,
				       "%s: timed out with incomplete data\n",
				       currenthost);
			if (debug) {
				printf("Received sequence numbers");
				for (n = 0; n <= MAXSEQ; n++)
				    if (haveseq[n])
					printf(" %d,", n);
				if (lastseq != 999)
				    printf(" last frame received\n");
				else
				    printf(" last frame not received\n");
			}
			return ERR_INCOMPLETE;
		}
	}

	n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
	if (n == -1) {
		warning("read", "", "");
		return -1;
	}


	/*
	 * Check for format errors.  Bug proofing.
	 */
	if (n < RESP_HEADER_SIZE) {
		if (debug)
		    printf("Short (%d byte) packet received\n", n);
		goto again;
	}
	if (INFO_VERSION(rpkt.rm_vn_mode) > NTP_VERSION ||
	    INFO_VERSION(rpkt.rm_vn_mode) < NTP_OLDVERSION) {
		if (debug)
		    printf("Packet received with version %d\n",
			   INFO_VERSION(rpkt.rm_vn_mode));
		goto again;
	}
	if (INFO_MODE(rpkt.rm_vn_mode) != MODE_PRIVATE) {
		if (debug)
		    printf("Packet received with mode %d\n",
			   INFO_MODE(rpkt.rm_vn_mode));
		goto again;
	}
	if (INFO_IS_AUTH(rpkt.auth_seq)) {
		if (debug)
		    printf("Encrypted packet received\n");
		goto again;
	}
	if (!ISRESPONSE(rpkt.rm_vn_mode)) {
		if (debug)
		    printf("Received request packet, wanted response\n");
		goto again;
	}
	if (INFO_MBZ(rpkt.mbz_itemsize) != 0) {
		if (debug)
		    printf("Received packet with nonzero MBZ field!\n");
		goto again;
	}

	/*
	 * Check implementation/request.  Could be old data getting to us.
	 */
	if (rpkt.implementation != implcode || rpkt.request != reqcode) {
		if (debug)
		    printf(
			    "Received implementation/request of %d/%d, wanted %d/%d",
			    rpkt.implementation, rpkt.request,
			    implcode, reqcode);
		goto again;
	}

	/*
	 * Check the error code.  If non-zero, return it.
	 */
	if (INFO_ERR(rpkt.err_nitems) != INFO_OKAY) {
		if (debug && ISMORE(rpkt.rm_vn_mode)) {
			printf("Error code %d received on not-final packet\n",
			       INFO_ERR(rpkt.err_nitems));
		}
		return (int)INFO_ERR(rpkt.err_nitems);
	}

	/*
	 * Collect items and size.  Make sure they make sense.
	 */
	items = INFO_NITEMS(rpkt.err_nitems);
	size = INFO_ITEMSIZE(rpkt.mbz_itemsize);
	if (esize > size)
		pad = esize - size;
	else 
		pad = 0;
	datasize = items * size;
	if ((size_t)datasize > (n-RESP_HEADER_SIZE)) {
		if (debug)
		    printf(
			    "Received items %d, size %d (total %d), data in packet is %d\n",
			    items, size, datasize, n-RESP_HEADER_SIZE);
		goto again;
	}

	/*
	 * If this isn't our first packet, make sure the size matches
	 * the other ones.
	 */
	if (!firstpkt && esize != *rsize) {
		if (debug)
		    printf("Received itemsize %d, previous %d\n",
			   size, *rsize);
		goto again;
	}
	/*
	 * If we've received this before, +toss it
	 */
	seq = INFO_SEQ(rpkt.auth_seq);
	if (haveseq[seq]) {
		if (debug)
		    printf("Received duplicate sequence number %d\n", seq);
		goto again;
	}
	haveseq[seq] = 1;

	/*
	 * If this is the last in the sequence, record that.
	 */
	if (!ISMORE(rpkt.rm_vn_mode)) {
		if (lastseq != 999) {
			printf("Received second end sequence packet\n");
			goto again;
		}
		lastseq = seq;
	}

	/*
	 * So far, so good.  Copy this data into the output array.
	 */
	if ((datap + datasize + (pad * items)) > (pktdata + pktdatasize)) {
		int offset = datap - pktdata;
		growpktdata();
		*rdata = pktdata; /* might have been realloced ! */
		datap = pktdata + offset;
	}
	/* 
	 * We now move the pointer along according to size and number of
	 * items.  This is so we can play nice with older implementations
	 */

	tmp_data = rpkt.data;
	for (i = 0; i < items; i++) {
		memcpy(datap, tmp_data, (unsigned)size);
		tmp_data += size;
		memset(datap + size, 0, pad);
		datap += size + pad;
	}

	if (firstpkt) {
		firstpkt = 0;
		*rsize = size + pad;
	}
	*ritems += items;

	/*
	 * Finally, check the count of received packets.  If we've got them
	 * all, return
	 */
	++numrecv;
	if (numrecv <= lastseq)
		goto again;
	return INFO_OKAY;
}
Esempio n. 2
0
/*
 * request - send a configuration request to the server, wait for a response
 */
static int
request(
    struct conf_peer *conf
)
{
    struct sock_timeval tvout;
    struct req_pkt reqpkt;
    size_t	req_len;
    size_t	total_len;	/* req_len plus keyid & digest */
    fd_set	fdset;
    l_fp	ts;
    char *	pch;
    char *	pchEnd;
    l_fp *	pts;
    keyid_t *pkeyid;
    int n;
#ifdef SYS_WINNT
    HANDLE	hReadWriteEvent = NULL;
    BOOL	ret;
    DWORD	NumberOfBytesWritten, NumberOfBytesRead, dwWait;
    OVERLAPPED overlap;
#endif /* SYS_WINNT */

    checkparent();		/* make sure our guy is still running */

    if (sockfd == INVALID_SOCKET)
        openntp();

#ifdef SYS_WINNT
    hReadWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
#endif /* SYS_WINNT */

    /*
     * Try to clear out any previously received traffic so it
     * doesn't fool us.  Note the socket is nonblocking.
     */
    tvout.tv_sec =  0;
    tvout.tv_usec = 0;
    FD_ZERO(&fdset);
    FD_SET(sockfd, &fdset);
    while (select(sockfd + 1, &fdset, (fd_set *)0, (fd_set *)0, &tvout) >
            0) {
        recv(sockfd, (char *)&reqpkt, sizeof(reqpkt), 0);
        FD_ZERO(&fdset);
        FD_SET(sockfd, &fdset);
    }

    /*
     * Make up a request packet with the configuration info
     */
    memset(&reqpkt, 0, sizeof(reqpkt));

    reqpkt.rm_vn_mode = RM_VN_MODE(0, 0, 0);
    reqpkt.auth_seq = AUTH_SEQ(1, 0);	/* authenticated, no seq */
    reqpkt.implementation = IMPL_XNTPD;	/* local implementation */
    reqpkt.request = REQ_CONFIG;		/* configure a new peer */
    reqpkt.err_nitems = ERR_NITEMS(0, 1);	/* one item */
    reqpkt.mbz_itemsize = MBZ_ITEMSIZE(sizeof(*conf));
    /* Make sure mbz_itemsize <= sizeof reqpkt.data */
    if (sizeof(*conf) > sizeof(reqpkt.data)) {
        msyslog(LOG_ERR,
                "Bletch: conf_peer is too big for reqpkt.data!");
        resolver_exit(1);
    }
    memcpy(reqpkt.data, conf, sizeof(*conf));

    if (sys_authenticate && req_hashlen > 16) {
        pch = reqpkt.data;
        /* 32-bit alignment */
        pch += (sizeof(*conf) + 3) & ~3;
        pts = (void *)pch;
        pkeyid = (void *)(pts + 1);
        pchEnd = (void *)pkeyid;
        req_len = pchEnd - (char *)&reqpkt;
        pchEnd = (void *)(pkeyid + 1);
        pchEnd += req_hashlen;
        total_len = pchEnd - (char *)&reqpkt;
        if (total_len > sizeof(reqpkt)) {
            msyslog(LOG_ERR,
                    "intres total_len %lu limit is %lu (%lu octet digest)\n",
                    (u_long)total_len,
                    (u_long)sizeof(reqpkt),
                    (u_long)req_hashlen);
            resolver_exit(1);
        }
    } else {
        pts = &reqpkt.tstamp;
        pkeyid = &reqpkt.keyid;
        req_len = REQ_LEN_NOMAC;
    }

    *pkeyid = htonl(req_keyid);
    get_systime(&ts);
    L_ADDUF(&ts, SKEWTIME);
    HTONL_FP(&ts, pts);
    if (sys_authenticate) {
        n = authencrypt(req_keyid, (void *)&reqpkt, req_len);
        if ((size_t)n != req_hashlen + sizeof(reqpkt.keyid)) {
            msyslog(LOG_ERR,
                    "intres maclen %d expected %u\n",
                    n, (u_long)(req_hashlen +
                                sizeof(reqpkt.keyid)));
            resolver_exit(1);
        }
        req_len += n;
    }

    /*
     * Done.  Send it.
     */
#ifndef SYS_WINNT
    n = send(sockfd, (char *)&reqpkt, req_len, 0);
    if (n < 0) {
        msyslog(LOG_ERR, "send to NTP server failed: %m");
        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 do 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
     */
    overlap.Offset = overlap.OffsetHigh = (DWORD)0;
    overlap.hEvent = hReadWriteEvent;
    ret = WriteFile((HANDLE)sockfd, (char *)&reqpkt, req_len,
                    NULL, (LPOVERLAPPED)&overlap);
    if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) {
        msyslog(LOG_ERR, "send to NTP server failed: %m");
        return 0;
    }
    dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000);
    if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) {
        if (dwWait == WAIT_FAILED)
            msyslog(LOG_ERR, "WaitForSingleObject failed: %m");
        return 0;
    }
    if (!GetOverlappedResult((HANDLE)sockfd, (LPOVERLAPPED)&overlap,
                             (LPDWORD)&NumberOfBytesWritten, FALSE)) {
        msyslog(LOG_ERR, "GetOverlappedResult for WriteFile fails: %m");
        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 (;;) {
        FD_ZERO(&fdset);
        FD_SET(sockfd, &fdset);
        tvout.tv_sec = TIMEOUT_SEC;
        tvout.tv_usec = TIMEOUT_USEC;

        n = select(sockfd + 1, &fdset, (fd_set *)0,
                   (fd_set *)0, &tvout);

        if (n < 0) {
            if (errno != EINTR)
                msyslog(LOG_ERR, "select() fails: %m");
            return 0;
        } else if (n == 0) {
#ifdef DEBUG
            if (debug)
                msyslog(LOG_INFO, "ntp_intres select() returned 0.");
#endif
            return 0;
        }

#ifndef SYS_WINNT
        n = recv(sockfd, (char *)&reqpkt, sizeof(reqpkt), 0);
        if (n <= 0) {
            if (n < 0) {
                msyslog(LOG_ERR, "recv() fails: %m");
                return 0;
            }
            continue;
        }
#else /* Overlapped I/O used on non-blocking sockets on Windows NT */
        ret = ReadFile((HANDLE)sockfd, (char *)&reqpkt, sizeof(reqpkt),
                       NULL, (LPOVERLAPPED)&overlap);
        if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) {
            msyslog(LOG_ERR, "ReadFile() fails: %m");
            return 0;
        }
        dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000);
        if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) {
            if (dwWait == WAIT_FAILED) {
                msyslog(LOG_ERR, "WaitForSingleObject for ReadFile fails: %m");
                return 0;
            }
            continue;
        }
        if (!GetOverlappedResult((HANDLE)sockfd, (LPOVERLAPPED)&overlap,
                                 (LPDWORD)&NumberOfBytesRead, FALSE)) {
            msyslog(LOG_ERR, "GetOverlappedResult fails: %m");
            return 0;
        }
        n = NumberOfBytesRead;
#endif /* SYS_WINNT */

        /*
         * Got one.  Check through to make sure it is what
         * we expect.
         */
        if (n < RESP_HEADER_SIZE) {
            msyslog(LOG_ERR, "received runt response (%d octets)",
                    n);
            continue;
        }

        if (!ISRESPONSE(reqpkt.rm_vn_mode)) {
#ifdef DEBUG
            if (debug > 1)
                msyslog(LOG_INFO, "received non-response packet");
#endif
            continue;
        }

        if (ISMORE(reqpkt.rm_vn_mode)) {
#ifdef DEBUG
            if (debug > 1)
                msyslog(LOG_INFO, "received fragmented packet");
#endif
            continue;
        }

        if ( ( (INFO_VERSION(reqpkt.rm_vn_mode) < 2)
                || (INFO_VERSION(reqpkt.rm_vn_mode) > NTP_VERSION))
                || INFO_MODE(reqpkt.rm_vn_mode) != MODE_PRIVATE) {
#ifdef DEBUG
            if (debug > 1)
                msyslog(LOG_INFO,
                        "version (%d/%d) or mode (%d/%d) incorrect",
                        INFO_VERSION(reqpkt.rm_vn_mode),
                        NTP_VERSION,
                        INFO_MODE(reqpkt.rm_vn_mode),
                        MODE_PRIVATE);
#endif
            continue;
        }

        if (INFO_SEQ(reqpkt.auth_seq) != 0) {
#ifdef DEBUG
            if (debug > 1)
                msyslog(LOG_INFO,
                        "nonzero sequence number (%d)",
                        INFO_SEQ(reqpkt.auth_seq));
#endif
            continue;
        }

        if (reqpkt.implementation != IMPL_XNTPD ||
                reqpkt.request != REQ_CONFIG) {
#ifdef DEBUG
            if (debug > 1)
                msyslog(LOG_INFO,
                        "implementation (%d) or request (%d) incorrect",
                        reqpkt.implementation, reqpkt.request);
#endif
            continue;
        }

        if (INFO_NITEMS(reqpkt.err_nitems) != 0 ||
                INFO_MBZ(reqpkt.mbz_itemsize) != 0 ||
                INFO_ITEMSIZE(reqpkt.mbz_itemsize) != 0) {
#ifdef DEBUG
            if (debug > 1)
                msyslog(LOG_INFO,
                        "nitems (%d) mbz (%d) or itemsize (%d) nonzero",
                        INFO_NITEMS(reqpkt.err_nitems),
                        INFO_MBZ(reqpkt.mbz_itemsize),
                        INFO_ITEMSIZE(reqpkt.mbz_itemsize));
#endif
            continue;
        }

        n = INFO_ERR(reqpkt.err_nitems);
        switch (n) {
        case INFO_OKAY:
            /* success */
            return 1;

        case INFO_ERR_NODATA:
            /*
             * newpeer() refused duplicate association, no
             * point in retrying so call it success.
             */
            return 1;

        case INFO_ERR_IMPL:
            msyslog(LOG_ERR,
                    "ntp_intres.request: implementation mismatch");
            return 0;

        case INFO_ERR_REQ:
            msyslog(LOG_ERR,
                    "ntp_intres.request: request unknown");
            return 0;

        case INFO_ERR_FMT:
            msyslog(LOG_ERR,
                    "ntp_intres.request: format error");
            return 0;

        case INFO_ERR_AUTH:
            msyslog(LOG_ERR,
                    "ntp_intres.request: permission denied");
            return 0;

        default:
            msyslog(LOG_ERR,
                    "ntp_intres.request: unknown error code %d", n);
            return 0;
        }
    }
}