process_private(
	struct recvbuf *rbufp,
	int mod_okay
	)
{
	static u_long quiet_until;
	struct req_pkt *inpkt;
	struct req_pkt_tail *tailinpkt;
	sockaddr_u *srcadr;
	endpt *inter;
	const struct req_proc *proc;
	int ec;
	short temp_size;
	l_fp ftmp;
	double dtemp;
	size_t recv_len;
	size_t noslop_len;
	size_t mac_len;

	/*
	 * Initialize pointers, for convenience
	 */
	recv_len = rbufp->recv_length;
	inpkt = (struct req_pkt *)&rbufp->recv_pkt;
	srcadr = &rbufp->recv_srcadr;
	inter = rbufp->dstadr;

	DPRINTF(3, ("process_private: impl %d req %d\n",
		    inpkt->implementation, inpkt->request));

	/*
	 * Do some sanity checks on the packet.  Return a format
	 * error if it fails.
	 */
	ec = 0;
	if (   (++ec, ISRESPONSE(inpkt->rm_vn_mode))
	    || (++ec, ISMORE(inpkt->rm_vn_mode))
	    || (++ec, INFO_VERSION(inpkt->rm_vn_mode) > NTP_VERSION)
	    || (++ec, INFO_VERSION(inpkt->rm_vn_mode) < NTP_OLDVERSION)
	    || (++ec, INFO_SEQ(inpkt->auth_seq) != 0)
	    || (++ec, INFO_ERR(inpkt->err_nitems) != 0)
	    || (++ec, INFO_MBZ(inpkt->mbz_itemsize) != 0)
	    || (++ec, rbufp->recv_length < (int)REQ_LEN_HDR)
		) {
		NLOG(NLOG_SYSEVENT)
			if (current_time >= quiet_until) {
				msyslog(LOG_ERR,
					"process_private: drop test %d"
					" failed, pkt from %s",
					ec, stoa(srcadr));
				quiet_until = current_time + 60;
			}
		return;
	}

	reqver = INFO_VERSION(inpkt->rm_vn_mode);

	/*
	 * Get the appropriate procedure list to search.
	 */
	if (inpkt->implementation == IMPL_UNIV)
		proc = univ_codes;
	else if ((inpkt->implementation == IMPL_XNTPD) ||
		 (inpkt->implementation == IMPL_XNTPD_OLD))
		proc = ntp_codes;
	else {
		req_ack(srcadr, inter, inpkt, INFO_ERR_IMPL);
		return;
	}

	/*
	 * Search the list for the request codes.  If it isn't one
	 * we know, return an error.
	 */
	while (proc->request_code != NO_REQUEST) {
		if (proc->request_code == (short) inpkt->request)
			break;
		proc++;
	}
	if (proc->request_code == NO_REQUEST) {
		req_ack(srcadr, inter, inpkt, INFO_ERR_REQ);
		return;
	}

	DPRINTF(4, ("found request in tables\n"));

	/*
	 * If we need data, check to see if we have some.  If we
	 * don't, check to see that there is none (picky, picky).
	 */	

	/* This part is a bit tricky, we want to be sure that the size
	 * returned is either the old or the new size.  We also can find
	 * out if the client can accept both types of messages this way. 
	 *
	 * Handle the exception of REQ_CONFIG. It can have two data sizes.
	 */
	temp_size = INFO_ITEMSIZE(inpkt->mbz_itemsize);
	if ((temp_size != proc->sizeofitem &&
	     temp_size != proc->v6_sizeofitem) &&
	    !(inpkt->implementation == IMPL_XNTPD &&
	      inpkt->request == REQ_CONFIG &&
	      temp_size == sizeof(struct old_conf_peer))) {
		DPRINTF(3, ("process_private: wrong item size, received %d, should be %d or %d\n",
			    temp_size, proc->sizeofitem, proc->v6_sizeofitem));
		req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
		return;
	}
	if ((proc->sizeofitem != 0) &&
	    ((size_t)(temp_size * INFO_NITEMS(inpkt->err_nitems)) >
	     (recv_len - REQ_LEN_HDR))) {
		DPRINTF(3, ("process_private: not enough data\n"));
		req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
		return;
	}

	switch (inpkt->implementation) {
	case IMPL_XNTPD:
		client_v6_capable = 1;
		break;
	case IMPL_XNTPD_OLD:
		client_v6_capable = 0;
		break;
	default:
		req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
		return;
	}

	/*
	 * If we need to authenticate, do so.  Note that an
	 * authenticatable packet must include a mac field, must
	 * have used key info_auth_keyid and must have included
	 * a time stamp in the appropriate field.  The time stamp
	 * must be within INFO_TS_MAXSKEW of the receive
	 * time stamp.
	 */
	if (proc->needs_auth && sys_authenticate) {

		if (recv_len < (REQ_LEN_HDR +
		    (INFO_ITEMSIZE(inpkt->mbz_itemsize) *
		    INFO_NITEMS(inpkt->err_nitems)) +
		    REQ_TAIL_MIN)) {
			req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
			return;
		}

		/*
		 * For 16-octet digests, regardless of itemsize and
		 * nitems, authenticated requests are a fixed size
		 * with the timestamp, key ID, and digest located
		 * at the end of the packet.  Because the key ID
		 * determining the digest size precedes the digest,
		 * for larger digests the fixed size request scheme
		 * is abandoned and the timestamp, key ID, and digest
		 * are located relative to the start of the packet,
		 * with the digest size determined by the packet size.
		 */
		noslop_len = REQ_LEN_HDR
			     + INFO_ITEMSIZE(inpkt->mbz_itemsize) *
			       INFO_NITEMS(inpkt->err_nitems)
			     + sizeof(inpkt->tstamp);
		/* 32-bit alignment */
		noslop_len = (noslop_len + 3) & ~3;
		if (recv_len > (noslop_len + MAX_MAC_LEN))
			mac_len = 20;
		else
			mac_len = recv_len - noslop_len;

		tailinpkt = (void *)((char *)inpkt + recv_len -
			    (mac_len + sizeof(inpkt->tstamp)));

		/*
		 * If this guy is restricted from doing this, don't let
		 * him.  If the wrong key was used, or packet doesn't
		 * have mac, return.
		 */
		if (!INFO_IS_AUTH(inpkt->auth_seq) || !info_auth_keyid
		    || ntohl(tailinpkt->keyid) != info_auth_keyid) {
			DPRINTF(5, ("failed auth %d info_auth_keyid %u pkt keyid %u maclen %lu\n",
				    INFO_IS_AUTH(inpkt->auth_seq),
				    info_auth_keyid,
				    ntohl(tailinpkt->keyid), (u_long)mac_len));
#ifdef DEBUG
			msyslog(LOG_DEBUG,
				"process_private: failed auth %d info_auth_keyid %u pkt keyid %u maclen %lu\n",
				INFO_IS_AUTH(inpkt->auth_seq),
				info_auth_keyid,
				ntohl(tailinpkt->keyid), (u_long)mac_len);
#endif
			req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
			return;
		}
		if (recv_len > REQ_LEN_NOMAC + MAX_MAC_LEN) {
			DPRINTF(5, ("bad pkt length %zu\n", recv_len));
			msyslog(LOG_ERR,
				"process_private: bad pkt length %zu",
				recv_len);
			req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
			return;
		}
		if (!mod_okay || !authhavekey(info_auth_keyid)) {
			DPRINTF(5, ("failed auth mod_okay %d\n",
				    mod_okay));
#ifdef DEBUG
			msyslog(LOG_DEBUG,
				"process_private: failed auth mod_okay %d\n",
				mod_okay);
#endif
			if (!mod_okay) {
				sys_restricted++;
			}
			req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
			return;
		}

		/*
		 * calculate absolute time difference between xmit time stamp
		 * and receive time stamp.  If too large, too bad.
		 */
		NTOHL_FP(&tailinpkt->tstamp, &ftmp);
		L_SUB(&ftmp, &rbufp->recv_time);
		LFPTOD(&ftmp, dtemp);
		if (fabs(dtemp) > INFO_TS_MAXSKEW) {
			/*
			 * He's a loser.  Tell him.
			 */
			DPRINTF(5, ("xmit/rcv timestamp delta %g > INFO_TS_MAXSKEW %g\n",
				    dtemp, INFO_TS_MAXSKEW));
			req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
			return;
		}

		/*
		 * So far so good.  See if decryption works out okay.
		 */
		if (!authdecrypt(info_auth_keyid, (u_int32 *)inpkt,
				 recv_len - mac_len, mac_len)) {
			DPRINTF(5, ("authdecrypt failed\n"));
			req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
			return;
		}
	}

	DPRINTF(3, ("process_private: all okay, into handler\n"));
	/*
	 * Packet is okay.  Call the handler to send him data.
	 */
	(proc->handler)(srcadr, inter, inpkt);
}