receive(
	struct recvbuf *rbufp
	)
{
	register struct peer *peer;	/* peer structure pointer */
	register struct pkt *pkt;	/* receive packet pointer */
	u_char	hisversion;		/* packet version */
	u_char	hisleap;		/* packet leap indicator */
	u_char	hismode;		/* packet mode */
	u_char	hisstratum;		/* packet stratum */
	u_short	restrict_mask;		/* restrict bits */
	const char *hm_str;		/* hismode string */
	const char *am_str;		/* association match string */
	int	kissCode = NOKISS;	/* Kiss Code */
	int	has_mac;		/* length of MAC field */
	int	authlen;		/* offset of MAC field */
	int	is_authentic = 0;	/* cryptosum ok */
	int	retcode = AM_NOMATCH;	/* match code */
	keyid_t	skeyid = 0;		/* key IDs */
	u_int32	opcode = 0;		/* extension field opcode */
	sockaddr_u *dstadr_sin;		/* active runway */
	struct peer *peer2;		/* aux peer structure pointer */
	endpt	*match_ep;		/* newpeer() local address */
	l_fp	p_org;			/* origin timestamp */
	l_fp	p_rec;			/* receive timestamp */
	l_fp	p_xmt;			/* transmit timestamp */
#ifdef AUTOKEY
	char	hostname[NTP_MAXSTRLEN + 1];
	char	*groupname = NULL;
	struct autokey *ap;		/* autokey structure pointer */
	int	rval;			/* cookie snatcher */
	keyid_t	pkeyid = 0, tkeyid = 0;	/* key IDs */
#endif	/* AUTOKEY */
#ifdef HAVE_NTP_SIGND
	static unsigned char zero_key[16];
#endif /* HAVE_NTP_SIGND */

	/*
	 * Monitor the packet and get restrictions. Note that the packet
	 * length for control and private mode packets must be checked
	 * by the service routines. Some restrictions have to be handled
	 * later in order to generate a kiss-o'-death packet.
	 */
	/*
	 * Bogus port check is before anything, since it probably
	 * reveals a clogging attack.
	 */
	sys_received++;
	if (0 == SRCPORT(&rbufp->recv_srcadr)) {
		sys_badlength++;
		return;				/* bogus port */
	}
	restrict_mask = restrictions(&rbufp->recv_srcadr);
	pkt = &rbufp->recv_pkt;
	DPRINTF(2, ("receive: at %ld %s<-%s flags %x restrict %03x org %#010x.%08x xmt %#010x.%08x\n",
		    current_time, stoa(&rbufp->dstadr->sin),
		    stoa(&rbufp->recv_srcadr), rbufp->dstadr->flags,
		    restrict_mask, ntohl(pkt->org.l_ui), ntohl(pkt->org.l_uf),
		    ntohl(pkt->xmt.l_ui), ntohl(pkt->xmt.l_uf)));
	hisversion = PKT_VERSION(pkt->li_vn_mode);
	hisleap = PKT_LEAP(pkt->li_vn_mode);
	hismode = (int)PKT_MODE(pkt->li_vn_mode);
	hisstratum = PKT_TO_STRATUM(pkt->stratum);
	if (restrict_mask & RES_IGNORE) {
		sys_restricted++;
		return;				/* ignore everything */
	}
	if (hismode == MODE_PRIVATE) {
		if (!ntp_mode7 || (restrict_mask & RES_NOQUERY)) {
			sys_restricted++;
			return;			/* no query private */
		}
		process_private(rbufp, ((restrict_mask &
		    RES_NOMODIFY) == 0));
		return;
	}
	if (hismode == MODE_CONTROL) {
		if (restrict_mask & RES_NOQUERY) {
			sys_restricted++;
			return;			/* no query control */
		}
		process_control(rbufp, restrict_mask);
		return;
	}
	if (restrict_mask & RES_DONTSERVE) {
		sys_restricted++;
		return;				/* no time serve */
	}

	/*
	 * This is for testing. If restricted drop ten percent of
	 * surviving packets.
	 */
	if (restrict_mask & RES_FLAKE) {
		if ((double)ntp_random() / 0x7fffffff < .1) {
			sys_restricted++;
			return;			/* no flakeway */
		}
	}

	/*
	 * Version check must be after the query packets, since they
	 * intentionally use an early version.
	 */
	if (hisversion == NTP_VERSION) {
		sys_newversion++;		/* new version */
	} else if (   !(restrict_mask & RES_VERSION)
		   && hisversion >= NTP_OLDVERSION) {
		sys_oldversion++;		/* previous version */
	} else {
		sys_badlength++;
		return;				/* old version */
	}

	/*
	 * Figure out his mode and validate the packet. This has some
	 * legacy raunch that probably should be removed. In very early
	 * NTP versions mode 0 was equivalent to what later versions
	 * would interpret as client mode.
	 */
	if (hismode == MODE_UNSPEC) {
		if (hisversion == NTP_OLDVERSION) {
			hismode = MODE_CLIENT;
		} else {
			sys_badlength++;
			return;                 /* invalid mode */
		}
	}

	/*
	 * Parse the extension field if present. We figure out whether
	 * an extension field is present by measuring the MAC size. If
	 * the number of words following the packet header is 0, no MAC
	 * is present and the packet is not authenticated. If 1, the
	 * packet is a crypto-NAK; if 3, the packet is authenticated
	 * with DES; if 5, the packet is authenticated with MD5; if 6,
	 * the packet is authenticated with SHA. If 2 or * 4, the packet
	 * is a runt and discarded forthwith. If greater than 6, an
	 * extension field is present, so we subtract the length of the
	 * field and go around again.
	 */
	authlen = LEN_PKT_NOMAC;
	has_mac = rbufp->recv_length - authlen;
	while (has_mac > 0) {
		u_int32	len;
#ifdef AUTOKEY
		u_int32	hostlen;
		struct exten *ep;
#endif /*AUTOKEY */

		if (has_mac % 4 != 0 || has_mac < (int)MIN_MAC_LEN) {
			sys_badlength++;
			return;			/* bad length */
		}
		if (has_mac <= (int)MAX_MAC_LEN) {
			skeyid = ntohl(((u_int32 *)pkt)[authlen / 4]);
			break;

		} else {
			opcode = ntohl(((u_int32 *)pkt)[authlen / 4]);
			len = opcode & 0xffff;
			if (   len % 4 != 0
			    || len < 4
			    || (int)len + authlen > rbufp->recv_length) {
				sys_badlength++;
				return;		/* bad length */
			}
#ifdef AUTOKEY
			/*
			 * Extract calling group name for later.  If
			 * sys_groupname is non-NULL, there must be
			 * a group name provided to elicit a response.
			 */
			if (   (opcode & 0x3fff0000) == CRYPTO_ASSOC
			    && sys_groupname != NULL) {
				ep = (struct exten *)&((u_int32 *)pkt)[authlen / 4];
				hostlen = ntohl(ep->vallen);
				if (   hostlen >= sizeof(hostname)
				    || hostlen > len -
						offsetof(struct exten, pkt)) {
					sys_badlength++;
					return;		/* bad length */
				}
				memcpy(hostname, &ep->pkt, hostlen);
				hostname[hostlen] = '\0';
				groupname = strchr(hostname, '@');
				if (groupname == NULL) {
					sys_declined++;
					return;
				}
				groupname++;
			}
#endif /* AUTOKEY */
			authlen += len;
			has_mac -= len;
		}
	}

	/*
	 * If has_mac is < 0 we had a malformed packet.
	 */
	if (has_mac < 0) {
		sys_badlength++;
		return;		/* bad length */
	}

	/*
	 * If authentication required, a MAC must be present.
	 */
	if (restrict_mask & RES_DONTTRUST && has_mac == 0) {
		sys_restricted++;
		return;				/* access denied */
	}

	/*
	 * Update the MRU list and finger the cloggers. It can be a
	 * little expensive, so turn it off for production use.
	 * RES_LIMITED and RES_KOD will be cleared in the returned
	 * restrict_mask unless one or both actions are warranted.
	 */
	restrict_mask = ntp_monitor(rbufp, restrict_mask);
	if (restrict_mask & RES_LIMITED) {
		sys_limitrejected++;
		if (   !(restrict_mask & RES_KOD)
		    || MODE_BROADCAST == hismode
		    || MODE_SERVER == hismode) {
			if (MODE_SERVER == hismode)
				DPRINTF(1, ("Possibly self-induced rate limiting of MODE_SERVER from %s\n",
					stoa(&rbufp->recv_srcadr)));
			return;			/* rate exceeded */
		}
		if (hismode == MODE_CLIENT)
			fast_xmit(rbufp, MODE_SERVER, skeyid,
			    restrict_mask);
		else
			fast_xmit(rbufp, MODE_ACTIVE, skeyid,
			    restrict_mask);
		return;				/* rate exceeded */
	}
	restrict_mask &= ~RES_KOD;

	/*
	 * We have tossed out as many buggy packets as possible early in
	 * the game to reduce the exposure to a clogging attack. Now we
	 * have to burn some cycles to find the association and
	 * authenticate the packet if required. Note that we burn only
	 * digest cycles, again to reduce exposure. There may be no
	 * matching association and that's okay.
	 *
	 * More on the autokey mambo. Normally the local interface is
	 * found when the association was mobilized with respect to a
	 * designated remote address. We assume packets arriving from
	 * the remote address arrive via this interface and the local
	 * address used to construct the autokey is the unicast address
	 * of the interface. However, if the sender is a broadcaster,
	 * the interface broadcast address is used instead.
	 * Notwithstanding this technobabble, if the sender is a
	 * multicaster, the broadcast address is null, so we use the
	 * unicast address anyway. Don't ask.
	 */
	peer = findpeer(rbufp,  hismode, &retcode);
	dstadr_sin = &rbufp->dstadr->sin;
	NTOHL_FP(&pkt->org, &p_org);
	NTOHL_FP(&pkt->rec, &p_rec);
	NTOHL_FP(&pkt->xmt, &p_xmt);
	hm_str = modetoa(hismode);
	am_str = amtoa(retcode);

	/*
	 * Authentication is conditioned by three switches:
	 *
	 * NOPEER  (RES_NOPEER) do not mobilize an association unless
	 *         authenticated
	 * NOTRUST (RES_DONTTRUST) do not allow access unless
	 *         authenticated (implies NOPEER)
	 * enable  (sys_authenticate) master NOPEER switch, by default
	 *         on
	 *
	 * The NOPEER and NOTRUST can be specified on a per-client basis
	 * using the restrict command. The enable switch if on implies
	 * NOPEER for all clients. There are four outcomes:
	 *
	 * NONE    The packet has no MAC.
	 * OK      the packet has a MAC and authentication succeeds
	 * ERROR   the packet has a MAC and authentication fails
	 * CRYPTO  crypto-NAK. The MAC has four octets only.
	 *
	 * Note: The AUTH(x, y) macro is used to filter outcomes. If x
	 * is zero, acceptable outcomes of y are NONE and OK. If x is
	 * one, the only acceptable outcome of y is OK.
	 */

	if (has_mac == 0) {
		restrict_mask &= ~RES_MSSNTP;
		is_authentic = AUTH_NONE; /* not required */
		DPRINTF(2, ("receive: at %ld %s<-%s mode %d/%s:%s len %d org %#010x.%08x xmt %#010x.%08x NOMAC\n",
			    current_time, stoa(dstadr_sin),
			    stoa(&rbufp->recv_srcadr), hismode, hm_str, am_str,
			    authlen,
			    ntohl(pkt->org.l_ui), ntohl(pkt->org.l_uf),
			    ntohl(pkt->xmt.l_ui), ntohl(pkt->xmt.l_uf)));
	} else if (has_mac == 4) {
		restrict_mask &= ~RES_MSSNTP;
		is_authentic = AUTH_CRYPTO; /* crypto-NAK */
		DPRINTF(2, ("receive: at %ld %s<-%s mode %d/%s:%s keyid %08x len %d auth %d org %#010x.%08x xmt %#010x.%08x MAC4\n",
			    current_time, stoa(dstadr_sin),
			    stoa(&rbufp->recv_srcadr), hismode, hm_str, am_str,
			    skeyid, authlen + has_mac, is_authentic,
			    ntohl(pkt->org.l_ui), ntohl(pkt->org.l_uf),
			    ntohl(pkt->xmt.l_ui), ntohl(pkt->xmt.l_uf)));

#ifdef HAVE_NTP_SIGND
		/*
		 * If the signature is 20 bytes long, the last 16 of
		 * which are zero, then this is a Microsoft client
		 * wanting AD-style authentication of the server's
		 * reply.
		 *
		 * This is described in Microsoft's WSPP docs, in MS-SNTP:
		 * http://msdn.microsoft.com/en-us/library/cc212930.aspx
		 */
	} else if (   has_mac == MAX_MD5_LEN
		   && (restrict_mask & RES_MSSNTP)
		   && (retcode == AM_FXMIT || retcode == AM_NEWPASS)
		   && (memcmp(zero_key, (char *)pkt + authlen + 4,
			      MAX_MD5_LEN - 4) == 0)) {
		is_authentic = AUTH_NONE;
#endif /* HAVE_NTP_SIGND */

	} else {
		restrict_mask &= ~RES_MSSNTP;
#ifdef AUTOKEY
		/*
		 * For autokey modes, generate the session key
		 * and install in the key cache. Use the socket
		 * broadcast or unicast address as appropriate.
		 */
		if (crypto_flags && skeyid > NTP_MAXKEY) {

			/*
			 * More on the autokey dance (AKD). A cookie is
			 * constructed from public and private values.
			 * For broadcast packets, the cookie is public
			 * (zero). For packets that match no
			 * association, the cookie is hashed from the
			 * addresses and private value. For server
			 * packets, the cookie was previously obtained
			 * from the server. For symmetric modes, the
			 * cookie was previously constructed using an
			 * agreement protocol; however, should PKI be
			 * unavailable, we construct a fake agreement as
			 * the EXOR of the peer and host cookies.
			 *
			 * hismode	ephemeral	persistent
			 * =======================================
			 * active	0		cookie#
			 * passive	0%		cookie#
			 * client	sys cookie	0%
			 * server	0%		sys cookie
			 * broadcast	0		0
			 *
			 * # if unsync, 0
			 * % can't happen
			 */
			if (has_mac < (int)MAX_MD5_LEN) {
				sys_badauth++;
				return;
			}
			if (hismode == MODE_BROADCAST) {

				/*
				 * For broadcaster, use the interface
				 * broadcast address when available;
				 * otherwise, use the unicast address
				 * found when the association was
				 * mobilized. However, if this is from
				 * the wildcard interface, game over.
				 */
				if (   crypto_flags
				    && rbufp->dstadr ==
				       ANY_INTERFACE_CHOOSE(&rbufp->recv_srcadr)) {
					sys_restricted++;
					return;	     /* no wildcard */
				}
				pkeyid = 0;
				if (!SOCK_UNSPEC(&rbufp->dstadr->bcast))
					dstadr_sin =
					    &rbufp->dstadr->bcast;
			} else if (peer == NULL) {
				pkeyid = session_key(
				    &rbufp->recv_srcadr, dstadr_sin, 0,
				    sys_private, 0);
			} else {
				pkeyid = peer->pcookie;
			}

			/*
			 * The session key includes both the public
			 * values and cookie. In case of an extension
			 * field, the cookie used for authentication
			 * purposes is zero. Note the hash is saved for
			 * use later in the autokey mambo.
			 */
			if (authlen > (int)LEN_PKT_NOMAC && pkeyid != 0) {
				session_key(&rbufp->recv_srcadr,
				    dstadr_sin, skeyid, 0, 2);
				tkeyid = session_key(
				    &rbufp->recv_srcadr, dstadr_sin,
				    skeyid, pkeyid, 0);
			} else {
				tkeyid = session_key(
				    &rbufp->recv_srcadr, dstadr_sin,
				    skeyid, pkeyid, 2);
			}

		}
#endif	/* AUTOKEY */

		/*
		 * Compute the cryptosum. Note a clogging attack may
		 * succeed in bloating the key cache. If an autokey,
		 * purge it immediately, since we won't be needing it
		 * again. If the packet is authentic, it can mobilize an
		 * association. Note that there is no key zero.
		 */
		if (!authdecrypt(skeyid, (u_int32 *)pkt, authlen,
		    has_mac))
			is_authentic = AUTH_ERROR;
		else
			is_authentic = AUTH_OK;
#ifdef AUTOKEY
		if (crypto_flags && skeyid > NTP_MAXKEY)
			authtrust(skeyid, 0);
#endif	/* AUTOKEY */
		DPRINTF(2, ("receive: at %ld %s<-%s mode %d/%s:%s keyid %08x len %d auth %d org %#010x.%08x xmt %#010x.%08x\n",
			    current_time, stoa(dstadr_sin),
			    stoa(&rbufp->recv_srcadr), hismode, hm_str, am_str,
			    skeyid, authlen + has_mac, is_authentic,
			    ntohl(pkt->org.l_ui), ntohl(pkt->org.l_uf),
			    ntohl(pkt->xmt.l_ui), ntohl(pkt->xmt.l_uf)));
	}

	/*
	 * The association matching rules are implemented by a set of
	 * routines and an association table. A packet matching an
	 * association is processed by the peer process for that
	 * association. If there are no errors, an ephemeral association
	 * is mobilized: a broadcast packet mobilizes a broadcast client
	 * aassociation; a manycast server packet mobilizes a manycast
	 * client association; a symmetric active packet mobilizes a
	 * symmetric passive association.
	 */
	switch (retcode) {

	/*
	 * This is a client mode packet not matching any association. If
	 * an ordinary client, simply toss a server mode packet back
	 * over the fence. If a manycast client, we have to work a
	 * little harder.
	 */
	case AM_FXMIT:

		/*
		 * If authentication OK, send a server reply; otherwise,
		 * send a crypto-NAK.
		 */
		if (!(rbufp->dstadr->flags & INT_MCASTOPEN)) {
			if (AUTH(restrict_mask & RES_DONTTRUST,
			   is_authentic)) {
				fast_xmit(rbufp, MODE_SERVER, skeyid,
				    restrict_mask);
			} else if (is_authentic == AUTH_ERROR) {
				fast_xmit(rbufp, MODE_SERVER, 0,
				    restrict_mask);
				sys_badauth++;
			} else {
				sys_restricted++;
			}
			return;			/* hooray */
		}

		/*
		 * This must be manycast. Do not respond if not
		 * configured as a manycast server.
		 */
		if (!sys_manycastserver) {
			sys_restricted++;
			return;			/* not enabled */
		}

#ifdef AUTOKEY
		/*
		 * Do not respond if not the same group.
		 */
		if (group_test(groupname, NULL)) {
			sys_declined++;
			return;
		}
#endif /* AUTOKEY */

		/*
		 * Do not respond if we are not synchronized or our
		 * stratum is greater than the manycaster or the
		 * manycaster has already synchronized to us.
		 */
		if (   sys_leap == LEAP_NOTINSYNC
		    || sys_stratum >= hisstratum
		    || (!sys_cohort && sys_stratum == hisstratum + 1)
		    || rbufp->dstadr->addr_refid == pkt->refid) {
			sys_declined++;
			return;			/* no help */
		}

		/*
		 * Respond only if authentication succeeds. Don't do a
		 * crypto-NAK, as that would not be useful.
		 */
		if (AUTH(restrict_mask & RES_DONTTRUST, is_authentic))
			fast_xmit(rbufp, MODE_SERVER, skeyid,
			    restrict_mask);
		return;				/* hooray */

	/*
	 * This is a server mode packet returned in response to a client
	 * mode packet sent to a multicast group address (for
	 * manycastclient) or to a unicast address (for pool). The
	 * origin timestamp is a good nonce to reliably associate the
	 * reply with what was sent. If there is no match, that's
	 * curious and could be an intruder attempting to clog, so we
	 * just ignore it.
	 *
	 * If the packet is authentic and the manycastclient or pool
	 * association is found, we mobilize a client association and
	 * copy pertinent variables from the manycastclient or pool
	 * association to the new client association. If not, just
	 * ignore the packet.
	 *
	 * There is an implosion hazard at the manycast client, since
	 * the manycast servers send the server packet immediately. If
	 * the guy is already here, don't fire up a duplicate.
	 */
	case AM_MANYCAST:

#ifdef AUTOKEY
		/*
		 * Do not respond if not the same group.
		 */
		if (group_test(groupname, NULL)) {
			sys_declined++;
			return;
		}
#endif /* AUTOKEY */
		if ((peer2 = findmanycastpeer(rbufp)) == NULL) {
			sys_restricted++;
			return;			/* not enabled */
		}
		if (!AUTH(  (!(peer2->cast_flags & MDF_POOL)
			     && sys_authenticate)
			  || (restrict_mask & (RES_NOPEER |
			      RES_DONTTRUST)), is_authentic)) {
			sys_restricted++;
			return;			/* access denied */
		}

		/*
		 * Do not respond if unsynchronized or stratum is below
		 * the floor or at or above the ceiling.
		 */
		if (   hisleap == LEAP_NOTINSYNC
		    || hisstratum < sys_floor
		    || hisstratum >= sys_ceiling) {
			sys_declined++;
			return;			/* no help */
		}
		peer = newpeer(&rbufp->recv_srcadr, NULL, rbufp->dstadr,
			       MODE_CLIENT, hisversion, peer2->minpoll,
			       peer2->maxpoll, FLAG_PREEMPT |
			       (FLAG_IBURST & peer2->flags), MDF_UCAST |
			       MDF_UCLNT, 0, skeyid, sys_ident);
		if (NULL == peer) {
			sys_declined++;
			return;			/* ignore duplicate  */
		}

		/*
		 * After each ephemeral pool association is spun,
		 * accelerate the next poll for the pool solicitor so
		 * the pool will fill promptly.
		 */
		if (peer2->cast_flags & MDF_POOL)
			peer2->nextdate = current_time + 1;

		/*
		 * Further processing of the solicitation response would
		 * simply detect its origin timestamp as bogus for the
		 * brand-new association (it matches the prototype
		 * association) and tinker with peer->nextdate delaying
		 * first sync.
		 */
		return;		/* solicitation response handled */

	/*
	 * This is the first packet received from a broadcast server. If
	 * the packet is authentic and we are enabled as broadcast
	 * client, mobilize a broadcast client association. We don't
	 * kiss any frogs here.
	 */
	case AM_NEWBCL:

#ifdef AUTOKEY
		/*
		 * Do not respond if not the same group.
		 */
		if (group_test(groupname, sys_ident)) {
			sys_declined++;
			return;
		}
#endif /* AUTOKEY */
		if (sys_bclient == 0) {
			sys_restricted++;
			return;			/* not enabled */
		}
		if (!AUTH(sys_authenticate | (restrict_mask &
		    (RES_NOPEER | RES_DONTTRUST)), is_authentic)) {
			sys_restricted++;
			return;			/* access denied */
		}

		/*
		 * Do not respond if unsynchronized or stratum is below
		 * the floor or at or above the ceiling.
		 */
		if (   hisleap == LEAP_NOTINSYNC
		    || hisstratum < sys_floor
		    || hisstratum >= sys_ceiling) {
			sys_declined++;
			return;			/* no help */
		}

#ifdef AUTOKEY
		/*
		 * Do not respond if Autokey and the opcode is not a
		 * CRYPTO_ASSOC response with association ID.
		 */
		if (   crypto_flags && skeyid > NTP_MAXKEY
		    && (opcode & 0xffff0000) != (CRYPTO_ASSOC | CRYPTO_RESP)) {
			sys_declined++;
			return;			/* protocol error */
		}
#endif	/* AUTOKEY */

		/*
		 * Broadcasts received via a multicast address may
		 * arrive after a unicast volley has begun
		 * with the same remote address.  newpeer() will not
		 * find duplicate associations on other local endpoints
		 * if a non-NULL endpoint is supplied.  multicastclient
		 * ephemeral associations are unique across all local
		 * endpoints.
		 */
		if (!(INT_MCASTOPEN & rbufp->dstadr->flags))
			match_ep = rbufp->dstadr;
		else
			match_ep = NULL;

		/*
		 * Determine whether to execute the initial volley.
		 */
		if (sys_bdelay != 0) {
#ifdef AUTOKEY
			/*
			 * If a two-way exchange is not possible,
			 * neither is Autokey.
			 */
			if (crypto_flags && skeyid > NTP_MAXKEY) {
				sys_restricted++;
				return;		/* no autokey */
			}
#endif	/* AUTOKEY */

			/*
			 * Do not execute the volley. Start out in
			 * broadcast client mode.
			 */
			peer = newpeer(&rbufp->recv_srcadr, NULL,
			    match_ep, MODE_BCLIENT, hisversion,
			    pkt->ppoll, pkt->ppoll, FLAG_PREEMPT,
			    MDF_BCLNT, 0, skeyid, sys_ident);
			if (NULL == peer) {
				sys_restricted++;
				return;		/* ignore duplicate */

			} else {
				peer->delay = sys_bdelay;
				peer->bxmt = p_xmt;
			}
			break;
		}

		/*
		 * Execute the initial volley in order to calibrate the
		 * propagation delay and run the Autokey protocol.
		 *
		 * Note that the minpoll is taken from the broadcast
		 * packet, normally 6 (64 s) and that the poll interval
		 * is fixed at this value.
		 */
		peer = newpeer(&rbufp->recv_srcadr, NULL, match_ep,
		    MODE_CLIENT, hisversion, pkt->ppoll, pkt->ppoll,
		    FLAG_BC_VOL | FLAG_IBURST | FLAG_PREEMPT, MDF_BCLNT,
		    0, skeyid, sys_ident);
		if (NULL == peer) {
			sys_restricted++;
			return;			/* ignore duplicate */
		}
		peer->bxmt = p_xmt;
#ifdef AUTOKEY
		if (skeyid > NTP_MAXKEY)
			crypto_recv(peer, rbufp);
#endif	/* AUTOKEY */

		return;				/* hooray */

	/*
	 * This is the first packet received from a symmetric active
	 * peer. If the packet is authentic and the first he sent,
	 * mobilize a passive association. If not, kiss the frog.
	 */
	case AM_NEWPASS:

#ifdef AUTOKEY
		/*
		 * Do not respond if not the same group.
		 */
		if (group_test(groupname, sys_ident)) {
			sys_declined++;
			return;
		}
#endif /* AUTOKEY */
		if (!AUTH(sys_authenticate | (restrict_mask &
		    (RES_NOPEER | RES_DONTTRUST)), is_authentic)) {

			/*
			 * If authenticated but cannot mobilize an
			 * association, send a symmetric passive
			 * response without mobilizing an association.
			 * This is for drat broken Windows clients. See
			 * Microsoft KB 875424 for preferred workaround.
			 */
			if (AUTH(restrict_mask & RES_DONTTRUST,
			    is_authentic)) {
				fast_xmit(rbufp, MODE_PASSIVE, skeyid,
				    restrict_mask);
				return;			/* hooray */
			}
			if (is_authentic == AUTH_ERROR) {
				fast_xmit(rbufp, MODE_ACTIVE, 0,
				    restrict_mask);
				sys_restricted++;
				return;
			}
			/* [Bug 2941]
			 * If we got here, the packet isn't part of an
			 * existing association, it isn't correctly
			 * authenticated, and it didn't meet either of
			 * the previous two special cases so we should
			 * just drop it on the floor.  For example,
			 * crypto-NAKs (is_authentic == AUTH_CRYPTO)
			 * will make it this far.  This is just
			 * debug-printed and not logged to avoid log
			 * flooding.
			 */
			DPRINTF(2, ("receive: at %ld refusing to mobilize passive association"
				    " with unknown peer %s mode %d/%s:%s keyid %08x len %d auth %d\n",
				    current_time, stoa(&rbufp->recv_srcadr),
				    hismode, hm_str, am_str, skeyid,
				    (authlen + has_mac), is_authentic));
			sys_declined++;
			return;
		}

		/*
		 * Do not respond if synchronized and if stratum is
		 * below the floor or at or above the ceiling. Note,
		 * this allows an unsynchronized peer to synchronize to
		 * us. It would be very strange if he did and then was
		 * nipped, but that could only happen if we were
		 * operating at the top end of the range.  It also means
		 * we will spin an ephemeral association in response to
		 * MODE_ACTIVE KoDs, which will time out eventually.
		 */
		if (   hisleap != LEAP_NOTINSYNC
		    && (hisstratum < sys_floor || hisstratum >= sys_ceiling)) {
			sys_declined++;
			return;			/* no help */
		}

		/*
		 * The message is correctly authenticated and allowed.
		 * Mobilize a symmetric passive association.
		 */
		if ((peer = newpeer(&rbufp->recv_srcadr, NULL,
		    rbufp->dstadr, MODE_PASSIVE, hisversion, pkt->ppoll,
		    NTP_MAXDPOLL, 0, MDF_UCAST, 0, skeyid,
		    sys_ident)) == NULL) {
			sys_declined++;
			return;			/* ignore duplicate */
		}
		break;

	/*
	 * Process regular packet. Nothing special.
	 */
	case AM_PROCPKT:

#ifdef AUTOKEY
		/*
		 * Do not respond if not the same group.
		 */
		if (group_test(groupname, peer->ident)) {
			sys_declined++;
			return;
		}
#endif /* AUTOKEY */

		if (MODE_BROADCAST == hismode) {
			u_char poll;
			int bail = 0;
			l_fp tdiff;

			DPRINTF(2, ("receive: PROCPKT/BROADCAST: prev pkt %ld seconds ago, ppoll: %d, %d secs\n",
				    (current_time - peer->timelastrec),
				    peer->ppoll, (1 << peer->ppoll)
				    ));
			/* Things we can check:
			 *
			 * Did the poll interval change?
			 * Is the poll interval in the packet in-range?
			 * Did this packet arrive too soon?
			 * Is the timestamp in this packet monotonic
			 *  with respect to the previous packet?
			 */

			/* This is noteworthy, not error-worthy */
			if (pkt->ppoll != peer->ppoll) {
				msyslog(LOG_INFO, "receive: broadcast poll from %s changed from %ud to %ud",
					stoa(&rbufp->recv_srcadr),
					peer->ppoll, pkt->ppoll);
			}

			poll = min(peer->maxpoll,
				   max(peer->minpoll, pkt->ppoll));

			/* This is error-worthy */
			if (pkt->ppoll != poll) {
				msyslog(LOG_INFO, "receive: broadcast poll of %ud from %s is out-of-range (%d to %d)!",
					pkt->ppoll, stoa(&rbufp->recv_srcadr),
					peer->minpoll, peer->maxpoll);
				++bail;
			}

			if (  (current_time - peer->timelastrec)
			    < (1 << pkt->ppoll)) {
				msyslog(LOG_INFO, "receive: broadcast packet from %s arrived after %ld, not %d seconds!",
					stoa(&rbufp->recv_srcadr),
					(current_time - peer->timelastrec),
					(1 << pkt->ppoll)
					);
				++bail;
			}

			tdiff = p_xmt;
			L_SUB(&tdiff, &peer->bxmt);
			if (tdiff.l_i < 0) {
				msyslog(LOG_INFO, "receive: broadcast packet from %s contains non-monotonic timestamp: %#010x.%08x -> %#010x.%08x",
					stoa(&rbufp->recv_srcadr),
					peer->bxmt.l_ui, peer->bxmt.l_uf,
					p_xmt.l_ui, p_xmt.l_uf
					);
				++bail;
			}

			peer->bxmt = p_xmt;

			if (bail) {
				peer->timelastrec = current_time;
				sys_declined++;
				return;
			}
		}

		break;

	/*
	 * A passive packet matches a passive association. This is
	 * usually the result of reconfiguring a client on the fly. As
	 * this association might be legitimate and this packet an
	 * attempt to deny service, just ignore it.
	 */
	case AM_ERR:
		sys_declined++;
		return;

	/*
	 * For everything else there is the bit bucket.
	 */
	default:
		sys_declined++;
		return;
	}

#ifdef AUTOKEY
	/*
	 * If the association is configured for Autokey, the packet must
	 * have a public key ID; if not, the packet must have a
	 * symmetric key ID.
	 */
	if (   is_authentic != AUTH_CRYPTO
	    && (   ((peer->flags & FLAG_SKEY) && skeyid <= NTP_MAXKEY)
	        || (!(peer->flags & FLAG_SKEY) && skeyid > NTP_MAXKEY))) {
		sys_badauth++;
		return;
	}
#endif	/* AUTOKEY */
	peer->received++;
	peer->flash &= ~PKT_TEST_MASK;
	if (peer->flags & FLAG_XBOGUS) {
		peer->flags &= ~FLAG_XBOGUS;
		peer->flash |= TEST3;
	}

	/*
	 * Next comes a rigorous schedule of timestamp checking. If the
	 * transmit timestamp is zero, the server has not initialized in
	 * interleaved modes or is horribly broken.
	 */
	if (L_ISZERO(&p_xmt)) {
		peer->flash |= TEST3;			/* unsynch */

	/*
	 * If the transmit timestamp duplicates a previous one, the
	 * packet is a replay. This prevents the bad guys from replaying
	 * the most recent packet, authenticated or not.
	 */
	} else if (L_ISEQU(&peer->xmt, &p_xmt)) {
		peer->flash |= TEST1;			/* duplicate */
		peer->oldpkt++;
		return;

	/*
	 * If this is a broadcast mode packet, skip further checking. If
	 * an initial volley, bail out now and let the client do its
	 * stuff. If the origin timestamp is nonzero, this is an
	 * interleaved broadcast. so restart the protocol.
	 */
	} else if (hismode == MODE_BROADCAST) {
		if (!L_ISZERO(&p_org) && !(peer->flags & FLAG_XB)) {
			peer->flags |= FLAG_XB;
			peer->aorg = p_xmt;
			peer->borg = rbufp->recv_time;
			report_event(PEVNT_XLEAVE, peer, NULL);
			return;
		}

	/*
	 * Basic mode checks:
	 *
	 * If there is no origin timestamp, it's either an initial packet
	 * or we've already received a response to our query.  Of course,
	 * should 'aorg' be all-zero because this really was the original
	 * transmit timestamp, we'll drop the reply.  There is a window of
	 * one nanosecond once every 136 years' time where this is possible.
	 * We currently ignore this situation.
	 *
	 * Otherwise, check for bogus packet in basic mode.
	 * If it is bogus, switch to interleaved mode and resynchronize,
	 * but only after confirming the packet is not bogus in
	 * symmetric interleaved mode.
	 *
	 * This could also mean somebody is forging packets claiming to
	 * be from us, attempting to cause our server to KoD us.
	 */
	} else if (peer->flip == 0) {
		if (0 < hisstratum && L_ISZERO(&p_org)) {
			L_CLR(&peer->aorg);
		} else if (    L_ISZERO(&peer->aorg)
			   || !L_ISEQU(&p_org, &peer->aorg)) {
			peer->bogusorg++;
			peer->flash |= TEST2;	/* bogus */
			msyslog(LOG_INFO,
				"receive: Unexpected origin timestamp %#010x.%08x from %s xmt %#010x.%08x",
				ntohl(pkt->org.l_ui), ntohl(pkt->org.l_uf),
				ntoa(&peer->srcadr),
				ntohl(pkt->xmt.l_ui), ntohl(pkt->xmt.l_uf));
			if (  !L_ISZERO(&peer->dst)
			    && L_ISEQU(&p_org, &peer->dst)) {
				/* Might be the start of an interleave */
				peer->flip = 1;
				report_event(PEVNT_XLEAVE, peer, NULL);
			}
			return; /* Bogus or possible interleave packet */
		} else {
			L_CLR(&peer->aorg);
		}

	/*
	 * Check for valid nonzero timestamp fields.
	 */
	} else if (L_ISZERO(&p_org) || L_ISZERO(&p_rec) ||
	    L_ISZERO(&peer->dst)) {
		peer->flash |= TEST3;		/* unsynch */

	/*
	 * Check for bogus packet in interleaved symmetric mode. This
	 * can happen if a packet is lost, duplicated or crossed. If
	 * found, flip and resynchronize.
	 */
	} else if (   !L_ISZERO(&peer->dst)
		   && !L_ISEQU(&p_org, &peer->dst)) {
		peer->bogusorg++;
		peer->flags |= FLAG_XBOGUS;
		peer->flash |= TEST2;		/* bogus */
		return; /* Bogus packet, we are done */
	}

	/*
	 * If this is a crypto_NAK, the server cannot authenticate a
	 * client packet. The server might have just changed keys. Clear
	 * the association and restart the protocol.
	 */
	if (is_authentic == AUTH_CRYPTO) {
		report_event(PEVNT_AUTH, peer, "crypto_NAK");
		peer->flash |= TEST5;		/* bad auth */
		peer->badauth++;
		if (peer->flags & FLAG_PREEMPT) {
			if (unpeer_crypto_nak_early) {
				unpeer(peer);
			}
			return;
		}
#ifdef AUTOKEY
		if (peer->crypto)
			peer_clear(peer, "AUTH");
#endif	/* AUTOKEY */
		return;

	/*
	 * If the digest fails or it's missing for authenticated
	 * associations, the client cannot authenticate a server
	 * reply to a client packet previously sent. The loopback check
	 * is designed to avoid a bait-and-switch attack, which was
	 * possible in past versions. If symmetric modes, return a
	 * crypto-NAK. The peer should restart the protocol.
	 */
	} else if (!AUTH(peer->keyid || has_mac ||
			 (restrict_mask & RES_DONTTRUST), is_authentic)) {
		report_event(PEVNT_AUTH, peer, "digest");
		peer->flash |= TEST5;		/* bad auth */
		peer->badauth++;
		if (   has_mac
		    && (hismode == MODE_ACTIVE || hismode == MODE_PASSIVE))
			fast_xmit(rbufp, MODE_ACTIVE, 0, restrict_mask);
		if (peer->flags & FLAG_PREEMPT) {
			if (unpeer_digest_early) {
				unpeer(peer);
			}
			return;
		}
#ifdef AUTOKEY
		if (peer->crypto)
			peer_clear(peer, "AUTH");
#endif	/* AUTOKEY */
		return;
	}

	/*
	 * Update the state variables.
	 */
	if (peer->flip == 0) {
		if (hismode != MODE_BROADCAST)
			peer->rec = p_xmt;
		peer->dst = rbufp->recv_time;
	}
	peer->xmt = p_xmt;

	/*
	 * Set the peer ppoll to the maximum of the packet ppoll and the
	 * peer minpoll. If a kiss-o'-death, set the peer minpoll to
	 * this maximum and advance the headway to give the sender some
	 * headroom. Very intricate.
	 */

	/*
	 * Check for any kiss codes. Note this is only used when a server
	 * responds to a packet request
	 */

	kissCode = kiss_code_check(hisleap, hisstratum, hismode, pkt->refid);

	/*
	 * Check to see if this is a RATE Kiss Code
	 * Currently this kiss code will accept whatever poll
	 * rate that the server sends
	 */
	peer->ppoll = max(peer->minpoll, pkt->ppoll);
	if (kissCode == RATEKISS) {
		peer->selbroken++;	/* Increment the KoD count */
		report_event(PEVNT_RATE, peer, NULL);
		if (pkt->ppoll > peer->minpoll)
			peer->minpoll = peer->ppoll;
		peer->burst = peer->retry = 0;
		peer->throttle = (NTP_SHIFT + 1) * (1 << peer->minpoll);
		poll_update(peer, pkt->ppoll);
		return;				/* kiss-o'-death */
	}
	if (kissCode != NOKISS) {
		peer->selbroken++;	/* Increment the KoD count */
		return;		/* Drop any other kiss code packets */
	}

	/*
	 * If:
	 *	- this is a *cast (uni-, broad-, or m-) server packet
	 *	- and it's authenticated
	 * then see if the sender's IP is trusted for this keyid.
	 * If it is, great - nothing special to do here.
	 * Otherwise, we should report and bail.
	 */

	switch (hismode) {
	    case MODE_SERVER:		/* server mode */
	    case MODE_BROADCAST:	/* broadcast mode */
	    case MODE_ACTIVE:		/* symmetric active mode */
		if (   is_authentic == AUTH_OK
		    && !authistrustedip(skeyid, &peer->srcadr)) {
			report_event(PEVNT_AUTH, peer, "authIP");
			peer->badauth++;
			return;
		}
	    	break;

	    case MODE_UNSPEC:		/* unspecified (old version) */
	    case MODE_PASSIVE:		/* symmetric passive mode */
	    case MODE_CLIENT:		/* client mode */
#if 0		/* At this point, MODE_CONTROL is overloaded by MODE_BCLIENT */
	    case MODE_CONTROL:		/* control mode */
#endif
	    case MODE_PRIVATE:		/* private mode */
	    case MODE_BCLIENT:		/* broadcast client mode */
	    	break;
	    default:
	    	break;
	}

	/*
	 * That was hard and I am sweaty, but the packet is squeaky
	 * clean. Get on with real work.
	 */
	peer->timereceived = current_time;
	peer->timelastrec = current_time;
	if (is_authentic == AUTH_OK)
		peer->flags |= FLAG_AUTHENTIC;
	else
		peer->flags &= ~FLAG_AUTHENTIC;

#ifdef AUTOKEY
	/*
	 * More autokey dance. The rules of the cha-cha are as follows:
	 *
	 * 1. If there is no key or the key is not auto, do nothing.
	 *
	 * 2. If this packet is in response to the one just previously
	 *    sent or from a broadcast server, do the extension fields.
	 *    Otherwise, assume bogosity and bail out.
	 *
	 * 3. If an extension field contains a verified signature, it is
	 *    self-authenticated and we sit the dance.
	 *
	 * 4. If this is a server reply, check only to see that the
	 *    transmitted key ID matches the received key ID.
	 *
	 * 5. Check to see that one or more hashes of the current key ID
	 *    matches the previous key ID or ultimate original key ID
	 *    obtained from the broadcaster or symmetric peer. If no
	 *    match, sit the dance and call for new autokey values.
	 *
	 * In case of crypto error, fire the orchestra, stop dancing and
	 * restart the protocol.
	 */
	if (peer->flags & FLAG_SKEY) {
		/*
		 * Decrement remaining autokey hashes. This isn't
		 * perfect if a packet is lost, but results in no harm.
		 */
		ap = (struct autokey *)peer->recval.ptr;
		if (ap != NULL) {
			if (ap->seq > 0)
				ap->seq--;
		}
		peer->flash |= TEST8;
		rval = crypto_recv(peer, rbufp);
		if (rval == XEVNT_OK) {
			peer->unreach = 0;
		} else {
			if (rval == XEVNT_ERR) {
				report_event(PEVNT_RESTART, peer,
				    "crypto error");
				peer_clear(peer, "CRYP");
				peer->flash |= TEST9;	/* bad crypt */
				if (peer->flags & FLAG_PREEMPT) {
					if (unpeer_crypto_early) {
						unpeer(peer);
					}
				}
			}
			return;
		}

		/*
		 * If server mode, verify the receive key ID matches
		 * the transmit key ID.
		 */
		if (hismode == MODE_SERVER) {
			if (skeyid == peer->keyid)
				peer->flash &= ~TEST8;

		/*
		 * If an extension field is present, verify only that it
		 * has been correctly signed. We don't need a sequence
		 * check here, but the sequence continues.
		 */
		} else if (!(peer->flash & TEST8)) {
			peer->pkeyid = skeyid;

		/*
		 * Now the fun part. Here, skeyid is the current ID in
		 * the packet, pkeyid is the ID in the last packet and
		 * tkeyid is the hash of skeyid. If the autokey values
		 * have not been received, this is an automatic error.
		 * If so, check that the tkeyid matches pkeyid. If not,
		 * hash tkeyid and try again. If the number of hashes
		 * exceeds the number remaining in the sequence, declare
		 * a successful failure and refresh the autokey values.
		 */
		} else if (ap != NULL) {
			int i;

			for (i = 0; ; i++) {
				if (   tkeyid == peer->pkeyid
				    || tkeyid == ap->key) {
					peer->flash &= ~TEST8;
					peer->pkeyid = skeyid;
					ap->seq -= i;
					break;
				}
				if (i > ap->seq) {
					peer->crypto &=
					    ~CRYPTO_FLAG_AUTO;
					break;
				}
				tkeyid = session_key(
				    &rbufp->recv_srcadr, dstadr_sin,
				    tkeyid, pkeyid, 0);
			}
			if (peer->flash & TEST8)
				report_event(PEVNT_AUTH, peer, "keylist");
		}
		if (!(peer->crypto & CRYPTO_FLAG_PROV)) /* test 9 */
			peer->flash |= TEST8;	/* bad autokey */

		/*
		 * The maximum lifetime of the protocol is about one
		 * week before restarting the Autokey protocol to
		 * refresh certificates and leapseconds values.
		 */
		if (current_time > peer->refresh) {
			report_event(PEVNT_RESTART, peer,
			    "crypto refresh");
			peer_clear(peer, "TIME");
			return;
		}
	}
#endif	/* AUTOKEY */

	/*
	 * The dance is complete and the flash bits have been lit. Toss
	 * the packet over the fence for processing, which may light up
	 * more flashers.
	 */
	process_packet(peer, pkt, rbufp->recv_length);

	/*
	 * In interleaved mode update the state variables. Also adjust the
	 * transmit phase to avoid crossover.
	 */
	if (peer->flip != 0) {
		peer->rec = p_rec;
		peer->dst = rbufp->recv_time;
		if (peer->nextdate - current_time < (1U << min(peer->ppoll,
		    peer->hpoll)) / 2)
			peer->nextdate++;
		else
			peer->nextdate--;
	}
}


test_EmptyKey(void)
{
	const keyid_t KEYNO = 3;
	const char* KEY = "";

	TEST_ASSERT_FALSE(authusekey(KEYNO, KEYTYPE, (const u_char*)KEY));

	return;
}


check_packet_format(NTP_Packet *message, int length)
{
  int version;

  /* Check version and length */

  version = NTP_LVM_TO_VERSION(message->lvm);
  if (version < NTP_MIN_COMPAT_VERSION || version > NTP_MAX_COMPAT_VERSION) {
    DEBUG_LOG(LOGF_NtpCore, "NTP packet has invalid version %d", version);
    return 0;
  }

  if (length < NTP_NORMAL_PACKET_LENGTH || (unsigned int)length % 4) {
    DEBUG_LOG(LOGF_NtpCore, "NTP packet has invalid length %d", length);
    return 0;
  }

  /* We can't reliably check the packet for invalid extension fields as we
     support MACs longer than the shortest valid extension field */

  return 1;
}


NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval)
{
  BroadcastDestination *destination;

  destination = (BroadcastDestination *)ARR_GetNewElement(broadcasts);

  destination->addr.ip_addr = *addr;
  destination->addr.port = port;
  destination->local_addr.ip_addr.family = IPADDR_UNSPEC;
  destination->local_addr.sock_fd = NIO_OpenServerSocket(&destination->addr);
  destination->interval = interval;

  SCH_AddTimeoutInClass(destination->interval, SAMPLING_SEPARATION, SAMPLING_RANDOMNESS,
                        SCH_NtpBroadcastClass, broadcast_timeout,
                        (void *)(long)(ARR_GetSize(broadcasts) - 1));
}


static int wddx_stack_push(wddx_stack *stack, void *element, int size)
{
	if (stack->top >= stack->max) {		/* we need to allocate more memory */
		stack->elements = (void **) erealloc(stack->elements,
				   (sizeof(void **) * (stack->max += STACK_BLOCK_SIZE)));
	}
	stack->elements[stack->top] = (void *) emalloc(size);
	memcpy(stack->elements[stack->top], element, size);
	return stack->top++;
}


static void php_wddx_serialize_boolean(wddx_packet *packet, zval *var)
{
	php_wddx_add_chunk(packet, Z_LVAL_P(var) ? WDDX_BOOLEAN_TRUE : WDDX_BOOLEAN_FALSE);
}


wddx_packet *php_wddx_constructor(void)
{
	smart_str *packet;

	packet = (smart_str *)emalloc(sizeof(smart_str));
	packet->c = NULL;

	return packet;
}


static void xmlrpc_server_destructor(zend_resource *rsrc)
{
	if (rsrc && rsrc->ptr) {
		rsrc->gc.refcount++;
		destroy_server_data((xmlrpc_server_data*) rsrc->ptr);
		rsrc->gc.refcount--;
	}
}


PHP_MINIT_FUNCTION(xmlrpc)
{
	le_xmlrpc_server = zend_register_list_destructors_ex(xmlrpc_server_destructor, NULL, "xmlrpc server", module_number);

	return SUCCESS;
}


static void destroy_server_data(xmlrpc_server_data *server)
{
	if (server) {
		XMLRPC_ServerDestroy(server->server_ptr);

		zval_ptr_dtor(&server->method_map);
		zval_ptr_dtor(&server->introspection_map);

		efree(server);
	}
}


void decode_request_worker(char *xml_in, int xml_in_len, char *encoding_in, zval* method_name_out, zval *retval) /* {{{ */
{
	XMLRPC_REQUEST response;
	STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS opts = {{0}};
	const char *method_name;
	opts.xml_elem_opts.encoding = encoding_in ? utf8_get_encoding_id_from_string(encoding_in) : ENCODING_DEFAULT;

	/* generate XMLRPC_REQUEST from raw xml */
	response = XMLRPC_REQUEST_FromXML(xml_in, xml_in_len, &opts);
	if (response) {
		ZVAL_NULL(retval);
		/* convert xmlrpc data to native php types */
		XMLRPC_to_PHP(XMLRPC_RequestGetData(response), retval);

		if (XMLRPC_RequestGetRequestType(response) == xmlrpc_request_call) {
			if (method_name_out) {
				method_name = XMLRPC_RequestGetMethodName(response);
				if (method_name) {
					zval_ptr_dtor(method_name_out);
					ZVAL_STRING(method_name_out, method_name);
				} else {
					zval_ptr_dtor(retval);
					ZVAL_NULL(retval);
				}
			}
		}

		/* dust, sweep, and mop */
		XMLRPC_RequestFree(response, 1);
	}
}


static void XMLRPC_to_PHP(XMLRPC_VALUE el, zval *elem)
{
	const char* pStr;

	if (el) {
		XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(el);

		switch (type) {
			case xmlrpc_empty:
				ZVAL_NULL(elem);
				break;
			case xmlrpc_string:
				pStr = XMLRPC_GetValueString(el);
				if (pStr) {
					ZVAL_STRINGL(elem, pStr, XMLRPC_GetValueStringLen(el));
				}
				break;
			case xmlrpc_int:
				ZVAL_LONG(elem, XMLRPC_GetValueInt(el));
				break;
			case xmlrpc_boolean:
				ZVAL_BOOL(elem, XMLRPC_GetValueBoolean(el));
				break;
			case xmlrpc_double:
				ZVAL_DOUBLE(elem, XMLRPC_GetValueDouble(el));
				break;
			case xmlrpc_datetime:
				ZVAL_STRINGL(elem, XMLRPC_GetValueDateTime_ISO8601(el), XMLRPC_GetValueStringLen(el));
				break;
			case xmlrpc_base64:
				pStr = XMLRPC_GetValueBase64(el);
				if (pStr) {
					ZVAL_STRINGL(elem, pStr, XMLRPC_GetValueStringLen(el));
				}
				break;
			case xmlrpc_vector:
				array_init(elem);
				{
					XMLRPC_VALUE xIter = XMLRPC_VectorRewind(el);

					while( xIter ) {
						zval val;
						ZVAL_UNDEF(&val);
						XMLRPC_to_PHP(xIter, &val);
						if (!Z_ISUNDEF(val)) {
							add_zval(elem, XMLRPC_GetValueID(xIter), &val);
						}
						xIter = XMLRPC_VectorNext(el);
					}
				}
				break;
			default:
				break;
		}
		set_zval_xmlrpc_type(elem, type);
	}
}


static XMLRPC_VALUE php_xmlrpc_callback(XMLRPC_SERVER server, XMLRPC_REQUEST xRequest, void* data) /* {{{ */
{
	xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
	zval* php_function;
	zval xmlrpc_params;
	zval callback_params[3];

	zval_ptr_dtor(&pData->xmlrpc_method);
	zval_ptr_dtor(&pData->return_data);

	/* convert xmlrpc to native php types */
	ZVAL_STRING(&pData->xmlrpc_method, XMLRPC_RequestGetMethodName(xRequest));
	XMLRPC_to_PHP(XMLRPC_RequestGetData(xRequest), &xmlrpc_params);

	/* check if the called method has been previous registered */
	if ((php_function = zend_hash_find(Z_ARRVAL(pData->server->method_map), Z_STR(pData->xmlrpc_method))) != NULL) {
		ZVAL_COPY_VALUE(&pData->php_function, php_function);
	}

	/* setup data hoojum */
	ZVAL_COPY_VALUE(&callback_params[0], &pData->xmlrpc_method);
	ZVAL_COPY_VALUE(&callback_params[1], &xmlrpc_params);
	ZVAL_COPY_VALUE(&callback_params[2], &pData->caller_params);

	/* Use same C function for all methods */

	/* php func prototype: function user_func($method_name, $xmlrpc_params, $user_params) */
	call_user_function(CG(function_table), NULL, &pData->php_function, &pData->return_data, 3, callback_params);

	pData->php_executed = 1;

	zval_ptr_dtor(&xmlrpc_params);

	return PHP_to_XMLRPC(&pData->return_data);
}


static void config_cb(const char *name, const char *value)
{
	if (!strcmp(name, "section") || !strcmp(name, "repo.group"))
		ctx.cfg.section = xstrdup(value);
	else if (!strcmp(name, "repo.url"))
		ctx.repo = cgit_add_repo(value);
	else if (ctx.repo && !strcmp(name, "repo.path"))
		ctx.repo->path = trim_end(value, '/');
	else if (ctx.repo && starts_with(name, "repo."))
		repo_config(ctx.repo, name + 5, value);
	else if (!strcmp(name, "readme"))
		string_list_append(&ctx.cfg.readme, xstrdup(value));
	else if (!strcmp(name, "root-title"))
		ctx.cfg.root_title = xstrdup(value);
	else if (!strcmp(name, "root-desc"))
		ctx.cfg.root_desc = xstrdup(value);
	else if (!strcmp(name, "root-readme"))
		ctx.cfg.root_readme = xstrdup(value);
	else if (!strcmp(name, "css"))
		ctx.cfg.css = xstrdup(value);
	else if (!strcmp(name, "favicon"))
		ctx.cfg.favicon = xstrdup(value);
	else if (!strcmp(name, "footer"))
		ctx.cfg.footer = xstrdup(value);
	else if (!strcmp(name, "head-include"))
		ctx.cfg.head_include = xstrdup(value);
	else if (!strcmp(name, "header"))
		ctx.cfg.header = xstrdup(value);
	else if (!strcmp(name, "logo"))
		ctx.cfg.logo = xstrdup(value);
	else if (!strcmp(name, "index-header"))
		ctx.cfg.index_header = xstrdup(value);
	else if (!strcmp(name, "index-info"))
		ctx.cfg.index_info = xstrdup(value);
	else if (!strcmp(name, "logo-link"))
		ctx.cfg.logo_link = xstrdup(value);
	else if (!strcmp(name, "module-link"))
		ctx.cfg.module_link = xstrdup(value);
	else if (!strcmp(name, "strict-export"))
		ctx.cfg.strict_export = xstrdup(value);
	else if (!strcmp(name, "virtual-root")) {
		ctx.cfg.virtual_root = ensure_end(value, '/');
	} else if (!strcmp(name, "nocache"))
		ctx.cfg.nocache = atoi(value);
	else if (!strcmp(name, "noplainemail"))
		ctx.cfg.noplainemail = atoi(value);
	else if (!strcmp(name, "noheader"))
		ctx.cfg.noheader = atoi(value);
	else if (!strcmp(name, "snapshots"))
		ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
	else if (!strcmp(name, "enable-filter-overrides"))
		ctx.cfg.enable_filter_overrides = atoi(value);
	else if (!strcmp(name, "enable-follow-links"))
		ctx.cfg.enable_follow_links = atoi(value);
	else if (!strcmp(name, "enable-http-clone"))
		ctx.cfg.enable_http_clone = atoi(value);
	else if (!strcmp(name, "enable-index-links"))
		ctx.cfg.enable_index_links = atoi(value);
	else if (!strcmp(name, "enable-index-owner"))
		ctx.cfg.enable_index_owner = atoi(value);
	else if (!strcmp(name, "enable-commit-graph"))
		ctx.cfg.enable_commit_graph = atoi(value);
	else if (!strcmp(name, "enable-log-filecount"))
		ctx.cfg.enable_log_filecount = atoi(value);
	else if (!strcmp(name, "enable-log-linecount"))
		ctx.cfg.enable_log_linecount = atoi(value);
	else if (!strcmp(name, "enable-remote-branches"))
		ctx.cfg.enable_remote_branches = atoi(value);
	else if (!strcmp(name, "enable-subject-links"))
		ctx.cfg.enable_subject_links = atoi(value);
	else if (!strcmp(name, "enable-tree-linenumbers"))
		ctx.cfg.enable_tree_linenumbers = atoi(value);
	else if (!strcmp(name, "enable-git-config"))
		ctx.cfg.enable_git_config = atoi(value);
	else if (!strcmp(name, "max-stats"))
		ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
	else if (!strcmp(name, "cache-size"))
		ctx.cfg.cache_size = atoi(value);
	else if (!strcmp(name, "cache-root"))
		ctx.cfg.cache_root = xstrdup(expand_macros(value));
	else if (!strcmp(name, "cache-root-ttl"))
		ctx.cfg.cache_root_ttl = atoi(value);
	else if (!strcmp(name, "cache-repo-ttl"))
		ctx.cfg.cache_repo_ttl = atoi(value);
	else if (!strcmp(name, "cache-scanrc-ttl"))
		ctx.cfg.cache_scanrc_ttl = atoi(value);
	else if (!strcmp(name, "cache-static-ttl"))
		ctx.cfg.cache_static_ttl = atoi(value);
	else if (!strcmp(name, "cache-dynamic-ttl"))
		ctx.cfg.cache_dynamic_ttl = atoi(value);
	else if (!strcmp(name, "cache-about-ttl"))
		ctx.cfg.cache_about_ttl = atoi(value);
	else if (!strcmp(name, "cache-snapshot-ttl"))
		ctx.cfg.cache_snapshot_ttl = atoi(value);
	else if (!strcmp(name, "case-sensitive-sort"))
		ctx.cfg.case_sensitive_sort = atoi(value);
	else if (!strcmp(name, "about-filter"))
		ctx.cfg.about_filter = cgit_new_filter(value, ABOUT);
	else if (!strcmp(name, "commit-filter"))
		ctx.cfg.commit_filter = cgit_new_filter(value, COMMIT);
	else if (!strcmp(name, "email-filter"))
		ctx.cfg.email_filter = cgit_new_filter(value, EMAIL);
	else if (!strcmp(name, "owner-filter"))
		ctx.cfg.owner_filter = cgit_new_filter(value, OWNER);
	else if (!strcmp(name, "auth-filter"))
		ctx.cfg.auth_filter = cgit_new_filter(value, AUTH);
	else if (!strcmp(name, "embedded"))
		ctx.cfg.embedded = atoi(value);
	else if (!strcmp(name, "max-atom-items"))
		ctx.cfg.max_atom_items = atoi(value);
	else if (!strcmp(name, "max-message-length"))
		ctx.cfg.max_msg_len = atoi(value);
	else if (!strcmp(name, "max-repodesc-length"))
		ctx.cfg.max_repodesc_len = atoi(value);
	else if (!strcmp(name, "max-blob-size"))
		ctx.cfg.max_blob_size = atoi(value);
	else if (!strcmp(name, "max-repo-count"))
		ctx.cfg.max_repo_count = atoi(value);
	else if (!strcmp(name, "max-commit-count"))
		ctx.cfg.max_commit_count = atoi(value);
	else if (!strcmp(name, "project-list"))
		ctx.cfg.project_list = xstrdup(expand_macros(value));
	else if (!strcmp(name, "scan-path"))
		if (!ctx.cfg.nocache && ctx.cfg.cache_size)
			process_cached_repolist(expand_macros(value));
		else if (ctx.cfg.project_list)
			scan_projects(expand_macros(value),
				      ctx.cfg.project_list, repo_config);
		else
			scan_tree(expand_macros(value), repo_config);
	else if (!strcmp(name, "scan-hidden-path"))
		ctx.cfg.scan_hidden_path = atoi(value);
	else if (!strcmp(name, "section-from-path"))
		ctx.cfg.section_from_path = atoi(value);
	else if (!strcmp(name, "repository-sort"))
		ctx.cfg.repository_sort = xstrdup(value);
	else if (!strcmp(name, "section-sort"))
		ctx.cfg.section_sort = atoi(value);
	else if (!strcmp(name, "source-filter"))
		ctx.cfg.source_filter = cgit_new_filter(value, SOURCE);
	else if (!strcmp(name, "summary-log"))
		ctx.cfg.summary_log = atoi(value);
	else if (!strcmp(name, "summary-branches"))
		ctx.cfg.summary_branches = atoi(value);
	else if (!strcmp(name, "summary-tags"))
		ctx.cfg.summary_tags = atoi(value);
	else if (!strcmp(name, "side-by-side-diffs"))
		ctx.cfg.difftype = atoi(value) ? DIFF_SSDIFF : DIFF_UNIFIED;
	else if (!strcmp(name, "agefile"))
		ctx.cfg.agefile = xstrdup(value);
	else if (!strcmp(name, "mimetype-file"))
		ctx.cfg.mimetype_file = xstrdup(value);
	else if (!strcmp(name, "renamelimit"))
		ctx.cfg.renamelimit = atoi(value);
	else if (!strcmp(name, "remove-suffix"))
		ctx.cfg.remove_suffix = atoi(value);
	else if (!strcmp(name, "robots"))
		ctx.cfg.robots = xstrdup(value);
	else if (!strcmp(name, "clone-prefix"))
		ctx.cfg.clone_prefix = xstrdup(value);
	else if (!strcmp(name, "clone-url"))
		ctx.cfg.clone_url = xstrdup(value);
	else if (!strcmp(name, "local-time"))
		ctx.cfg.local_time = atoi(value);
	else if (!strcmp(name, "commit-sort")) {
		if (!strcmp(value, "date"))
			ctx.cfg.commit_sort = 1;
		if (!strcmp(value, "topo"))
			ctx.cfg.commit_sort = 2;
	} else if (!strcmp(name, "branch-sort")) {
		if (!strcmp(value, "age"))
			ctx.cfg.branch_sort = 1;
		if (!strcmp(value, "name"))
			ctx.cfg.branch_sort = 0;
	} else if (starts_with(name, "mimetype."))
		add_mimetype(name + 9, value);
	else if (!strcmp(name, "include"))
		parse_configfile(expand_macros(value), config_cb);
}


static void add_mimetype(const char *name, const char *value)
{
	struct string_list_item *item;

	item = string_list_insert(&ctx.cfg.mimetypes, xstrdup(name));
	item->util = xstrdup(value);
}


static char *get_first_line(char *txt)
{
	char *t = xstrdup(txt);
	char *p = strchr(t, '\n');
	if (p)
		*p = '\0';
	return t;
}


char *cgit_pageurl(const char *reponame, const char *pagename,
		   const char *query)
{
	return cgit_fileurl(reponame, pagename, NULL, query);
}


static void cgit_print_path_crumbs(char *path)
{
	char *old_path = ctx.qry.path;
	char *p = path, *q, *end = path + strlen(path);

	ctx.qry.path = NULL;
	cgit_self_link("root", NULL, NULL);
	ctx.qry.path = p = path;
	while (p < end) {
		if (!(q = strchr(p, '/')))
			q = end;
		*q = '\0';
		html_txt("/");
		cgit_self_link(p, NULL, NULL);
		if (q < end)
			*q = '/';
		p = q + 1;
	}
	ctx.qry.path = old_path;
}


void html_intoption(int value, const char *text, int selected_value)
{
	htmlf("<option value='%d'%s>", value,
	      value == selected_value ? " selected='selected'" : "");
	html_txt(text);
	html("</option>");
}


int http_parse_querystring(const char *txt_, void (*fn)(const char *name, const char *value))
{
	char *o, *t, *txt, *value = NULL, c;

	if (!txt_)
		return 0;

	o = t = txt = xstrdup(txt_);
	while ((c=*t) != '\0') {
		if (c == '=') {
			*t = '\0';
			value = t + 1;
		} else if (c == '+') {
			*t = ' ';
		} else if (c == '%') {
			t = convert_query_hexchar(t);
		} else if (c == '&') {
			*t = '\0';
			(*fn)(txt, value);
			txt = t + 1;
			value = NULL;
		}
		t++;
	}
	if (t != txt)
		(*fn)(txt, value);
	free(o);
	return 0;
}


char *cgit_hosturl(void)
{
	if (ctx.env.http_host)
		return xstrdup(ctx.env.http_host);
	if (!ctx.env.server_name)
		return NULL;
	if (!ctx.env.server_port || atoi(ctx.env.server_port) == 80)
		return xstrdup(ctx.env.server_name);
	return fmtalloc("%s:%s", ctx.env.server_name, ctx.env.server_port);
}


void html_header_arg_in_quotes(const char *txt)
{
	const char *t = txt;
	while (t && *t) {
		unsigned char c = *t;
		const char *e = NULL;
		if (c == '\\')
			e = "\\\\";
		else if (c == '\r')
			e = "\\r";
		else if (c == '\n')
			e = "\\n";
		else if (c == '"')
			e = "\\\"";
		if (e) {
			html_raw(txt, t - txt);
			html(e);
			txt = t + 1;
		}
		t++;
	}
	if (t != txt)
		html(txt);

}


int html_include(const char *filename)
{
	FILE *f;
	char buf[4096];
	size_t len;

	if (!(f = fopen(filename, "r"))) {
		fprintf(stderr, "[cgit] Failed to include file %s: %s (%d).\n",
			filename, strerror(errno), errno);
		return -1;
	}
	while ((len = fread(buf, 1, 4096, f)) > 0)
		html_raw(buf, len);
	fclose(f);
	return 0;
}


void cgit_print_age(time_t t, time_t max_relative, const char *format)
{
	time_t now, secs;

	if (!t)
		return;
	time(&now);
	secs = now - t;
	if (secs < 0)
		secs = 0;

	if (secs > max_relative && max_relative >= 0) {
		html("");
		cgit_print_date(t, format, ctx.cfg.local_time);
		html("");
		return;
	}

	if (secs < TM_HOUR * 2) {
		print_rel_date(t, secs * 1.0 / TM_MIN, "age-mins", "min.");
		return;
	}
	if (secs < TM_DAY * 2) {
		print_rel_date(t, secs * 1.0 / TM_HOUR, "age-hours", "hours");
		return;
	}
	if (secs < TM_WEEK * 2) {
		print_rel_date(t, secs * 1.0 / TM_DAY, "age-days", "days");
		return;
	}
	if (secs < TM_MONTH * 2) {
		print_rel_date(t, secs * 1.0 / TM_WEEK, "age-weeks", "weeks");
		return;
	}
	if (secs < TM_YEAR * 2) {
		print_rel_date(t, secs * 1.0 / TM_MONTH, "age-months", "months");
		return;
	}
	print_rel_date(t, secs * 1.0 / TM_YEAR, "age-years", "years");
}


PyImaging_XbmEncoderNew(PyObject* self, PyObject* args)
{
    ImagingEncoderObject* encoder;

    encoder = PyImaging_EncoderNew(0);
    if (encoder == NULL)
	return NULL;

    if (get_packer(encoder, "1", "1;R") < 0)
	return NULL;

    encoder->encode = ImagingXbmEncode;

    return (PyObject*) encoder;
}


PyImaging_GifEncoderNew(PyObject* self, PyObject* args)
{
    ImagingEncoderObject* encoder;

    char *mode;
    char *rawmode;
    int bits = 8;
    int interlace = 0;
    if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &bits, &interlace))
	return NULL;

    encoder = PyImaging_EncoderNew(sizeof(GIFENCODERSTATE));
    if (encoder == NULL)
	return NULL;

    if (get_packer(encoder, mode, rawmode) < 0)
	return NULL;

    encoder->encode = ImagingGifEncode;

    ((GIFENCODERSTATE*)encoder->state.context)->bits = bits;
    ((GIFENCODERSTATE*)encoder->state.context)->interlace = interlace;

    return (PyObject*) encoder;
}


PyImaging_PcxDecoderNew(PyObject* self, PyObject* args)
{
    ImagingDecoderObject* decoder;

    char* mode;
    char* rawmode;
    int stride;
    if (!PyArg_ParseTuple(args, "ssi", &mode, &rawmode, &stride))
        return NULL;

    decoder = PyImaging_DecoderNew(0);
    if (decoder == NULL)
        return NULL;

    if (get_unpacker(decoder, mode, rawmode) < 0)
        return NULL;

    decoder->state.bytes = stride;

    decoder->decode = ImagingPcxDecode;

    return (PyObject*) decoder;
}


get_packer(ImagingEncoderObject* encoder, const char* mode,
           const char* rawmode)
{
    int bits;
    ImagingShuffler pack;

    pack = ImagingFindPacker(mode, rawmode, &bits);
    if (!pack) {
	Py_DECREF(encoder);
	PyErr_SetString(PyExc_SystemError, "unknown raw mode");
	return -1;
    }

    encoder->state.shuffle = pack;
    encoder->state.bits = bits;

    return 0;
}


nextvar(
	int *datalen,
	const char **datap,
	char **vname,
	char **vvalue
	)
{
	const char *cp;
	const char *np;
	const char *cpend;
	size_t srclen;
	size_t len;
	static char name[MAXVARLEN];
	static char value[MAXVALLEN];

	cp = *datap;
	cpend = cp + *datalen;

	/*
	 * Space past commas and white space
	 */
	while (cp < cpend && (*cp == ',' || isspace((int)*cp)))
		cp++;
	if (cp >= cpend)
		return 0;

	/*
	 * Copy name until we hit a ',', an '=', a '\r' or a '\n'.  Backspace
	 * over any white space and terminate it.
	 */
	srclen = strcspn(cp, ",=\r\n");
	srclen = min(srclen, (size_t)(cpend - cp));
	len = srclen;
	while (len > 0 && isspace((unsigned char)cp[len - 1]))
		len--;
	if (len >= sizeof(name))
	    return 0;
	if (len > 0)
		memcpy(name, cp, len);
	name[len] = '\0';
	*vname = name;
	cp += srclen;

	/*
	 * Check if we hit the end of the buffer or a ','.  If so we are done.
	 */
	if (cp >= cpend || *cp == ',' || *cp == '\r' || *cp == '\n') {
		if (cp < cpend)
			cp++;
		*datap = cp;
		*datalen = cpend - cp;
		*vvalue = NULL;
		return 1;
	}

	/*
	 * So far, so good.  Copy out the value
	 */
	cp++;	/* past '=' */
	while (cp < cpend && (isspace((unsigned char)*cp) && *cp != '\r' && *cp != '\n'))
		cp++;
	np = cp;
	if ('"' == *np) {
		do {
			np++;
		} while (np < cpend && '"' != *np);
		if (np < cpend && '"' == *np)
			np++;
	} else {
		while (np < cpend && ',' != *np && '\r' != *np)
			np++;
	}
	len = np - cp;
	if (np > cpend || len >= sizeof(value) ||
	    (np < cpend && ',' != *np && '\r' != *np))
		return 0;
	memcpy(value, cp, len);
	/*
	 * Trim off any trailing whitespace
	 */
	while (len > 0 && isspace((unsigned char)value[len - 1]))
		len--;
	value[len] = '\0';

	/*
	 * Return this.  All done.
	 */
	if (np < cpend && ',' == *np)
		np++;
	*datap = np;
	*datalen = cpend - np;
	*vvalue = value;
	return 1;
}


create_peer_node(
	int		hmode,
	address_node *	addr,
	attr_val_fifo *	options
	)
{
	peer_node *my_node;
	attr_val *option;
	int freenode;
	int errflag = 0;

	my_node = emalloc_zero(sizeof(*my_node));

	/* Initialize node values to default */
	my_node->peerversion = NTP_VERSION;

	/* Now set the node to the read values */
	my_node->host_mode = hmode;
	my_node->addr = addr;

	/*
	 * the options FIFO mixes items that will be saved in the
	 * peer_node as explicit members, such as minpoll, and
	 * those that are moved intact to the peer_node's peerflags
	 * FIFO.  The options FIFO is consumed and reclaimed here.
	 */

	if (options != NULL)
		CHECK_FIFO_CONSISTENCY(*options);
	while (options != NULL) {
		UNLINK_FIFO(option, *options, link);
		if (NULL == option) {
			free(options);
			break;
		}

		freenode = 1;
		/* Check the kind of option being set */
		switch (option->attr) {

		case T_Flag:
			APPEND_G_FIFO(my_node->peerflags, option);
			freenode = 0;
			break;

		case T_Minpoll:
			if (option->value.i < NTP_MINPOLL ||
			    option->value.i > UCHAR_MAX) {
				msyslog(LOG_INFO,
					"minpoll: provided value (%d) is out of range [%d-%d])",
					option->value.i, NTP_MINPOLL,
					UCHAR_MAX);
				my_node->minpoll = NTP_MINPOLL;
			} else {
				my_node->minpoll =
					(u_char)option->value.u;
			}
			break;

		case T_Maxpoll:
			if (option->value.i < 0 ||
			    option->value.i > NTP_MAXPOLL) {
				msyslog(LOG_INFO,
					"maxpoll: provided value (%d) is out of range [0-%d])",
					option->value.i, NTP_MAXPOLL);
				my_node->maxpoll = NTP_MAXPOLL;
			} else {
				my_node->maxpoll =
					(u_char)option->value.u;
			}
			break;

		case T_Ttl:
			if (option->value.u >= MAX_TTL) {
				msyslog(LOG_ERR, "ttl: invalid argument");
				errflag = 1;
			} else {
				my_node->ttl = (u_char)option->value.u;
			}
			break;

		case T_Mode:
			my_node->ttl = option->value.u;
			break;

		case T_Key:
			if (option->value.u >= KEYID_T_MAX) {
				msyslog(LOG_ERR, "key: invalid argument");
				errflag = 1;
			} else {
				my_node->peerkey =
					(keyid_t)option->value.u;
			}
			break;

		case T_Version:
			if (option->value.u >= UCHAR_MAX) {
				msyslog(LOG_ERR, "version: invalid argument");
				errflag = 1;
			} else {
				my_node->peerversion =
					(u_char)option->value.u;
			}
			break;

		case T_Ident:
			my_node->group = option->value.s;
			break;

		default:
			msyslog(LOG_ERR,
				"Unknown peer/server option token %s",
				token_name(option->attr));
			errflag = 1;
		}
		if (freenode)
			free(option);
	}

	/* Check if errors were reported. If yes, ignore the node */
	if (errflag) {
		free(my_node);
		my_node = NULL;
	}

	return my_node;
}


append_gen_fifo(
	void *fifo,
	void *entry
	)
{
	gen_fifo *pf;
	gen_node *pe;

	pf = fifo;
	pe = entry;
	if (NULL == pf)
		pf = emalloc_zero(sizeof(*pf));
	else
		CHECK_FIFO_CONSISTENCY(*pf);
	if (pe != NULL)
		LINK_FIFO(*pf, pe, link);
	CHECK_FIFO_CONSISTENCY(*pf);

	return pf;
}


create_sim_script_info(
	double		duration,
	attr_val_fifo *	script_queue
	)
{
	script_info *my_info;
	attr_val *my_attr_val;

	my_info = emalloc_zero(sizeof(*my_info));

	/* Initialize Script Info with default values*/
	my_info->duration = duration;
	my_info->prop_delay = NET_DLY;
	my_info->proc_delay = PROC_DLY;

	/* Traverse the script_queue and fill out non-default values */

	for (my_attr_val = HEAD_PFIFO(script_queue);
	     my_attr_val != NULL;
	     my_attr_val = my_attr_val->link) {

		/* Set the desired value */
		switch (my_attr_val->attr) {

		case T_Freq_Offset:
			my_info->freq_offset = my_attr_val->value.d;
			break;

		case T_Wander:
			my_info->wander = my_attr_val->value.d;
			break;

		case T_Jitter:
			my_info->jitter = my_attr_val->value.d;
			break;

		case T_Prop_Delay:
			my_info->prop_delay = my_attr_val->value.d;
			break;

		case T_Proc_Delay:
			my_info->proc_delay = my_attr_val->value.d;
			break;

		default:
			msyslog(LOG_ERR, "Unknown script token %d",
				my_attr_val->attr);
		}
	}

	return my_info;
}


normal_dtoa(
	double d
	)
{
	char *	buf;
	char *	pch_e;
	char *	pch_nz;

	LIB_GETBUF(buf);
	snprintf(buf, LIB_BUFLENGTH, "%g", d);

	/* use lowercase 'e', strip any leading zeroes in exponent */
	pch_e = strchr(buf, 'e');
	if (NULL == pch_e) {
		pch_e = strchr(buf, 'E');
		if (NULL == pch_e)
			return buf;
		*pch_e = 'e';
	}
	pch_e++;
	if ('-' == *pch_e)
		pch_e++;
	pch_nz = pch_e;
	while ('0' == *pch_nz)
		pch_nz++;
	if (pch_nz == pch_e)
		return buf;
	strlcpy(pch_e, pch_nz, LIB_BUFLENGTH - (pch_e - buf));

	return buf;
}


destroy_address_node(
	address_node *my_node
	)
{
	if (NULL == my_node)
		return;
	REQUIRE(NULL != my_node->address);

	free(my_node->address);
	free(my_node);
}


destroy_addr_opts_fifo(
	addr_opts_fifo *	fifo
	)
{
	addr_opts_node *	aon;

	if (fifo != NULL) {
		for (;;) {
			UNLINK_FIFO(aon, *fifo, link);
			if (aon == NULL)
				break;
			destroy_address_node(aon->addr);
			destroy_attr_val_fifo(aon->options);
			free(aon);
		}
		free(fifo);
	}
}


config_ttl(
	config_tree *ptree
	)
{
	size_t i = 0;
	int_node *curr_ttl;

	curr_ttl = HEAD_PFIFO(ptree->ttl);
	for (; curr_ttl != NULL; curr_ttl = curr_ttl->link) {
		if (i < COUNTOF(sys_ttl))
			sys_ttl[i++] = (u_char)curr_ttl->i;
		else
			msyslog(LOG_INFO,
				"ttl: Number of TTL entries exceeds %zu. Ignoring TTL %d...",
				COUNTOF(sys_ttl), curr_ttl->i);
	}
	sys_ttlmax = i - 1;
}


create_sim_server(
	address_node *		addr,
	double			server_offset,
	script_info_fifo *	script
	)
{
	server_info *my_info;

	my_info = emalloc_zero(sizeof(*my_info));
	my_info->server_time = server_offset;
	my_info->addr = get_next_address(addr);
	my_info->script = script;
	UNLINK_FIFO(my_info->curr_script, *my_info->script, link);

	return my_info;
}


create_attr_uval(
	int	attr,
	u_int	value
	)
{
	attr_val *my_val;

	my_val = emalloc_zero(sizeof(*my_val));
	my_val->attr = attr;
	my_val->value.u = value;
	my_val->type = T_U_int;

	return my_val;
}


void stw_le_phys(AddressSpace *as, hwaddr addr, uint32_t val)
{
    stw_phys_internal(as, addr, val, DEVICE_LITTLE_ENDIAN);
}


static inline void stl_phys_internal(AddressSpace *as,
                                     hwaddr addr, uint32_t val,
                                     enum device_endian endian)
{
    uint8_t *ptr;
    MemoryRegion *mr;
    hwaddr l = 4;
    hwaddr addr1;

    mr = address_space_translate(as, addr, &addr1, &l,
                                 true);
    if (l < 4 || !memory_access_is_direct(mr, true)) {
#if defined(TARGET_WORDS_BIGENDIAN)
        if (endian == DEVICE_LITTLE_ENDIAN) {
            val = bswap32(val);
        }
#else
        if (endian == DEVICE_BIG_ENDIAN) {
            val = bswap32(val);
        }
#endif
        io_mem_write(mr, addr1, val, 4);
    } else {
        /* RAM case */
        addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK;
        ptr = qemu_get_ram_ptr(addr1);
        switch (endian) {
        case DEVICE_LITTLE_ENDIAN:
            stl_le_p(ptr, val);
            break;
        case DEVICE_BIG_ENDIAN:
            stl_be_p(ptr, val);
            break;
        default:
            stl_p(ptr, val);
            break;
        }
        invalidate_and_set_dirty(addr1, 4);
    }
}


static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length)
{
    ram_addr_t start1;
    RAMBlock *block;
    ram_addr_t end;

    end = TARGET_PAGE_ALIGN(start + length);
    start &= TARGET_PAGE_MASK;

    rcu_read_lock();
    block = qemu_get_ram_block(start);
    assert(block == qemu_get_ram_block(end - 1));
    start1 = (uintptr_t)ramblock_ptr(block, start - block->offset);
    cpu_tlb_reset_dirty_all(start1, length);
    rcu_read_unlock();
}


static uint64_t subpage_read(void *opaque, hwaddr addr,
                             unsigned len)
{
    subpage_t *subpage = opaque;
    uint8_t buf[8];

#if defined(DEBUG_SUBPAGE)
    printf("%s: subpage %p len %u addr " TARGET_FMT_plx "\n", __func__,
           subpage, len, addr);
#endif
    address_space_read(subpage->as, addr + subpage->base, buf, len);
    switch (len) {
    case 1:
        return ldub_p(buf);
    case 2:
        return lduw_p(buf);
    case 4:
        return ldl_p(buf);
    case 8:
        return ldq_p(buf);
    default:
        abort();
    }
}


static void *qemu_ram_ptr_length(ram_addr_t addr, hwaddr *size)
{
    void *ptr;
    if (*size == 0) {
        return NULL;
    }
    if (xen_enabled()) {
        return xen_map_cache(addr, *size, 1);
    } else {
        RAMBlock *block;
        rcu_read_lock();
        QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
            if (addr - block->offset < block->max_length) {
                if (addr - block->offset + *size > block->max_length)
                    *size = block->max_length - addr + block->offset;
                ptr = ramblock_ptr(block, addr - block->offset);
                rcu_read_unlock();
                return ptr;
            }
        }

        fprintf(stderr, "Bad ram offset %" PRIx64 "\n", (uint64_t)addr);
        abort();
    }
}


void stb_phys(AddressSpace *as, hwaddr addr, uint32_t val)
{
    uint8_t v = val;
    address_space_rw(as, addr, &v, 1, 1);
}


void qemu_ram_unset_idstr(ram_addr_t addr)
{
    RAMBlock *block;

    /* FIXME: arch_init.c assumes that this is not called throughout
     * migration.  Ignore the problem since hot-unplug during migration
     * does not work anyway.
     */

    rcu_read_lock();
    block = find_ram_block(addr);
    if (block) {
        memset(block->idstr, 0, sizeof(block->idstr));
    }
    rcu_read_unlock();
}


int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len,
                          int flags, CPUWatchpoint **watchpoint)
{
    CPUWatchpoint *wp;

    /* forbid ranges which are empty or run off the end of the address space */
    if (len == 0 || (addr + len - 1) < addr) {
        error_report("tried to set invalid watchpoint at %"
                     VADDR_PRIx ", len=%" VADDR_PRIu, addr, len);
        return -EINVAL;
    }
    wp = g_malloc(sizeof(*wp));

    wp->vaddr = addr;
    wp->len = len;
    wp->flags = flags;

    /* keep all GDB-injected watchpoints in front */
    if (flags & BP_GDB) {
        QTAILQ_INSERT_HEAD(&cpu->watchpoints, wp, entry);
    } else {
        QTAILQ_INSERT_TAIL(&cpu->watchpoints, wp, entry);
    }

    tlb_flush_page(cpu, addr);

    if (watchpoint)
        *watchpoint = wp;
    return 0;
}


MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
                                      hwaddr *xlat, hwaddr *plen,
                                      bool is_write)
{
    IOMMUTLBEntry iotlb;
    MemoryRegionSection *section;
    MemoryRegion *mr;

    rcu_read_lock();
    for (;;) {
        AddressSpaceDispatch *d = atomic_rcu_read(&as->dispatch);
        section = address_space_translate_internal(d, addr, &addr, plen, true);
        mr = section->mr;

        if (!mr->iommu_ops) {
            break;
        }

        iotlb = mr->iommu_ops->translate(mr, addr, is_write);
        addr = ((iotlb.translated_addr & ~iotlb.addr_mask)
                | (addr & iotlb.addr_mask));
        *plen = MIN(*plen, (addr | iotlb.addr_mask) - addr + 1);
        if (!(iotlb.perm & (1 << is_write))) {
            mr = &io_mem_unassigned;
            break;
        }

        as = iotlb.target_as;
    }

    if (xen_enabled() && memory_access_is_direct(mr, is_write)) {
        hwaddr page = ((addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE) - addr;
        *plen = MIN(page, *plen);
    }

    *xlat = addr;
    rcu_read_unlock();
    return mr;
}


MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
                                      hwaddr *xlat, hwaddr *plen,
                                      bool is_write)
{
    IOMMUTLBEntry iotlb;
    MemoryRegionSection *section;
    MemoryRegion *mr;

    for (;;) {
        AddressSpaceDispatch *d = atomic_rcu_read(&as->dispatch);
        section = address_space_translate_internal(d, addr, &addr, plen, true);
        mr = section->mr;

        if (!mr->iommu_ops) {
            break;
        }

        iotlb = mr->iommu_ops->translate(mr, addr, is_write);
        addr = ((iotlb.translated_addr & ~iotlb.addr_mask)
                | (addr & iotlb.addr_mask));
        *plen = MIN(*plen, (addr | iotlb.addr_mask) - addr + 1);
        if (!(iotlb.perm & (1 << is_write))) {
            mr = &io_mem_unassigned;
            break;
        }

        as = iotlb.target_as;
    }

    if (xen_enabled() && memory_access_is_direct(mr, is_write)) {
        hwaddr page = ((addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE) - addr;
        *plen = MIN(page, *plen);
    }

    *xlat = addr;
    return mr;
}


void stq_phys(AddressSpace *as, hwaddr addr, uint64_t val)
{
    address_space_stq(as, addr, val, MEMTXATTRS_UNSPECIFIED, NULL);
}


void address_space_stq(AddressSpace *as, hwaddr addr, uint64_t val,
                       MemTxAttrs attrs, MemTxResult *result)
{
    MemTxResult r;
    val = tswap64(val);
    r = address_space_rw(as, addr, attrs, (void *) &val, 8, 1);
    if (result) {
        *result = r;
    }
}


static inline void address_space_stw_internal(AddressSpace *as,
                                              hwaddr addr, uint32_t val,
                                              MemTxAttrs attrs,
                                              MemTxResult *result,
                                              enum device_endian endian)
{
    uint8_t *ptr;
    MemoryRegion *mr;
    hwaddr l = 2;
    hwaddr addr1;
    MemTxResult r;
    bool release_lock = false;

    rcu_read_lock();
    mr = address_space_translate(as, addr, &addr1, &l, true);
    if (l < 2 || !memory_access_is_direct(mr, true)) {
        release_lock |= prepare_mmio_access(mr);

#if defined(TARGET_WORDS_BIGENDIAN)
        if (endian == DEVICE_LITTLE_ENDIAN) {
            val = bswap16(val);
        }
#else
        if (endian == DEVICE_BIG_ENDIAN) {
            val = bswap16(val);
        }
#endif
        r = memory_region_dispatch_write(mr, addr1, val, 2, attrs);
    } else {
        /* RAM case */
        addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK;
        ptr = qemu_get_ram_ptr(addr1);
        switch (endian) {
        case DEVICE_LITTLE_ENDIAN:
            stw_le_p(ptr, val);
            break;
        case DEVICE_BIG_ENDIAN:
            stw_be_p(ptr, val);
            break;
        default:
            stw_p(ptr, val);
            break;
        }
        invalidate_and_set_dirty(mr, addr1, 2);
        r = MEMTX_OK;
    }
    if (result) {
        *result = r;
    }
    if (release_lock) {
        qemu_mutex_unlock_iothread();
    }
    rcu_read_unlock();
}


DH_Server::DH_Server()
    : signature_(0), length_(0), keyMessage_(0)
{}


void buildSHA_CertVerify(SSL& ssl, byte* digest)
{
    opaque sha_result[SHA_LEN];
    opaque sha_inner[SECRET_LEN + PAD_SHA];
    opaque sha_outer[SECRET_LEN + PAD_SHA + SHA_LEN];

    const opaque* master_secret =
        ssl.getSecurity().get_connection().master_secret_;

     // make sha inner
    memcpy(sha_inner, master_secret, SECRET_LEN);
    memcpy(&sha_inner[SECRET_LEN], PAD1, PAD_SHA);

    ssl.useHashes().use_SHA().get_digest(sha_result, sha_inner,
                                         sizeof(sha_inner));

    // make sha outer
    memcpy(sha_outer, master_secret, SECRET_LEN);
    memcpy(&sha_outer[SECRET_LEN], PAD2, PAD_SHA);
    memcpy(&sha_outer[SECRET_LEN + PAD_SHA], sha_result, SHA_LEN);

    ssl.useHashes().use_SHA().get_digest(digest, sha_outer, sizeof(sha_outer));
}


void ServerKeyExchange::Process(input_buffer& input, SSL& ssl)
{
    if (input.get_error()) {
        ssl.SetError(bad_input);
        return;
    }
    createKey(ssl);
    if (ssl.GetError()) return;
    server_key_->read(ssl, input);
    if (input.get_error()) {
        ssl.SetError(bad_input);
        return;
    }

    ssl.useStates().useClient() = serverKeyExchangeComplete;
}


int HandShakeBase::get_length() const
{
    return length_;
}


void Parameters::SetCipherNames()
{
    const int suites = suites_size_ / 2;
    int pos = 0;

    for (int j = 0; j < suites; j++) {
        int index = suites_[j*2 + 1];  // every other suite is suite id
        size_t len = strlen(cipher_names[index]) + 1;
        strncpy(cipher_list_[pos++], cipher_names[index], len);
    }
    cipher_list_[pos][0] = 0;
}


void CertificateVerify::Build(SSL& ssl)
{
    build_certHashes(ssl, hashes_);

    uint16 sz = 0;
    byte   len[VERIFY_HEADER];
    mySTL::auto_array<byte> sig;

    // sign
    const CertManager& cert = ssl.getCrypto().get_certManager();
    if (cert.get_keyType() == rsa_sa_algo) {
        RSA rsa(cert.get_privateKey(), cert.get_privateKeyLength(), false);

        sz = rsa.get_cipherLength() + VERIFY_HEADER;
        sig.reset(NEW_YS byte[sz]);

        c16toa(sz - VERIFY_HEADER, len);
        memcpy(sig.get(), len, VERIFY_HEADER);
        rsa.sign(sig.get() + VERIFY_HEADER, hashes_.md5_, sizeof(Hashes),
                 ssl.getCrypto().get_random());
        // check for rsa signautre fault
        if (!rsa.verify(hashes_.md5_, sizeof(Hashes), sig.get() + VERIFY_HEADER,
                                                      rsa.get_cipherLength())) {
            ssl.SetError(rsaSignFault_error);
            return;
        }
    }
    else {  // DSA
        DSS dss(cert.get_privateKey(), cert.get_privateKeyLength(), false);

        sz = DSS_SIG_SZ + DSS_ENCODED_EXTRA + VERIFY_HEADER;
        sig.reset(NEW_YS byte[sz]);

        c16toa(sz - VERIFY_HEADER, len);
        memcpy(sig.get(), len, VERIFY_HEADER);
        dss.sign(sig.get() + VERIFY_HEADER, hashes_.sha_, SHA_LEN,
                 ssl.getCrypto().get_random());

        byte encoded[DSS_SIG_SZ + DSS_ENCODED_EXTRA];
        TaoCrypt::EncodeDSA_Signature(sig.get() + VERIFY_HEADER, encoded);
        memcpy(sig.get() + VERIFY_HEADER, encoded, sizeof(encoded));
    }
    set_length(sz);
    signature_ = sig.release();
}


void ServerKeyExchange::build(SSL& ssl)
{
    server_key_->build(ssl);
    set_length(server_key_->get_length());
}


int ClientDiffieHellmanPublic::get_length() const
{
    return length_;
}


output_buffer& CertificateRequest::get(output_buffer& out) const
{
    return out << *this;
}


static int constant_compare(const byte* a, const byte* b, int len)
{
    int good = 0;
    int bad  = 0;

    for (int i = 0; i < len; i++) {
        if (a[i] == b[i])
            good++;
        else
            bad++;
    }

    if (good == len)
        return 0;
    else
        return 0 - bad;  // failure
}


ServerKeyBase* CreateRSAServerKEA()       { return NEW_YS RSA_Server; }


uint16 ChangeCipherSpec::get_length() const
{
    return SIZEOF_ENUM;
}


ServerKeyExchange::ServerKeyExchange(SSL& ssl)
{
    createKey(ssl);
}


HandShakeType ClientHello::get_type() const
{
    return client_hello;
}


void EncryptedPreMasterSecret::read(SSL& ssl, input_buffer& input)
{
    if (input.get_error()) {
        ssl.SetError(bad_input);
        return;
    }

    const CertManager& cert = ssl.getCrypto().get_certManager();
    RSA rsa(cert.get_privateKey(), cert.get_privateKeyLength(), false);
    uint16 cipherLen = rsa.get_cipherLength();
    if (ssl.isTLS()) {
        byte len[2];
        len[0] = input[AUTO];
        len[1] = input[AUTO];
        ato16(len, cipherLen);
    }
    alloc(cipherLen);
    input.read(secret_, length_);
    if (input.get_error()) {
        ssl.SetError(bad_input);
        return;
    }

    opaque preMasterSecret[SECRET_LEN];
    memset(preMasterSecret, 0, sizeof(preMasterSecret));
    rsa.decrypt(preMasterSecret, secret_, length_,
                ssl.getCrypto().get_random());

    ProtocolVersion pv = ssl.getSecurity().get_connection().chVersion_;
    if (pv.major_ != preMasterSecret[0] || pv.minor_ != preMasterSecret[1])
        ssl.SetError(pms_version_error); // continue deriving for timing attack

    ssl.set_preMaster(preMasterSecret, SECRET_LEN);
    ssl.makeMasterSecret();
}


int sendAlert(SSL& ssl, const Alert& alert)
{
    output_buffer out;
    buildAlert(ssl, out, alert);
    ssl.Send(out.get_buffer(), out.get_size());

    return alert.get_length();
}


Connection::Connection(ProtocolVersion v, RandomPool& ran)
    : pre_master_secret_(0), sequence_number_(0), peer_sequence_number_(0),
      pre_secret_len_(0), send_server_key_(false), master_clean_(false),
      TLS_(v.major_ >= 3 && v.minor_ >= 1),
      TLSv1_1_(v.major_ >= 3 && v.minor_ >= 2), compression_(false),
      version_(v), random_(ran)
{
    memset(sessionID_, 0, sizeof(sessionID_));
}


void buildOutput(output_buffer& buffer, const RecordLayerHeader& rlHdr,
                 const HandShakeHeader& hsHdr, const HandShakeBase& shake)
{
    buffer.allocate(RECORD_HEADER + rlHdr.length_);
    buffer << rlHdr << hsHdr << shake;
}


static inline void compress_rounds(SSL& ssl, int rounds, const byte* dummy)
{
    if (rounds) {
        Digest* digest = NULL;

        MACAlgorithm ma = ssl.getSecurity().get_parms().mac_algorithm_;
        if (ma == sha)
            digest = NEW_YS SHA;
        else if (ma == md5)
            digest = NEW_YS MD5;
        else if (ma == rmd)
            digest = NEW_YS RMD;
        else
            return;

        for (int i = 0; i < rounds; i++)
            digest->update(dummy, COMPRESS_LOWER);

        ysDelete(digest);
    }
}


input_buffer& operator>>(input_buffer& input, Message& msg)
{
    return msg.set(input);
}


opaque* ServerDHParams::alloc_pub(int sz)
{
    Ys_ = NEW_YS opaque[pubSz_ = sz];
    return Ys_;
}


static int sanity_check_message(SSL& ssl, uint msgSz)
{
    uint minSz = 0;

    if (ssl.getSecurity().get_parms().cipher_type_ == block) {
        uint blockSz = ssl.getCrypto().get_cipher().get_blockSize();
        if (msgSz % blockSz)
            return -1;

        minSz = ssl.getSecurity().get_parms().hash_size_ + 1;  // pad byte too
        if (blockSz > minSz)
            minSz = blockSz;

        if (ssl.isTLSv1_1())
            minSz += blockSz;   // explicit IV
    }
    else {      // stream
        minSz = ssl.getSecurity().get_parms().hash_size_;
    }

    if (msgSz < minSz)
        return -1;

    return 0;
}


output_buffer& HandShakeBase::get(output_buffer& out) const
{
    return out;
}


uint16 Data::get_length() const
{
    return length_;
}


const opaque* ClientKeyExchange::getKey() const
{
    return client_key_->get_clientKey();
}


input_buffer& Finished::set(input_buffer& in)
{
    return in  >> *this;
}


Finished::Finished()
{
    set_length(FINISHED_SZ);
}


static MYSQL_RES * cli_use_result(MYSQL *mysql)
{
  MYSQL_RES *result;
  DBUG_ENTER("cli_use_result");

  if (!mysql->fields)
    DBUG_RETURN(0);
  if (mysql->status != MYSQL_STATUS_GET_RESULT)
  {
    set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate);
    DBUG_RETURN(0);
  }
  if (!(result=(MYSQL_RES*) my_malloc(sizeof(*result)+
				      sizeof(ulong)*mysql->field_count,
				      MYF(MY_WME | MY_ZEROFILL))))
    DBUG_RETURN(0);
  result->lengths=(ulong*) (result+1);
  result->methods= mysql->methods;
  if (!(result->row=(MYSQL_ROW)
	my_malloc(sizeof(result->row[0])*(mysql->field_count+1), MYF(MY_WME))))
  {					/* Ptrs: to one row */
    my_free(result);
    DBUG_RETURN(0);
  }
  result->fields=	mysql->fields;
  result->field_alloc=	mysql->field_alloc;
  result->field_count=	mysql->field_count;
  result->current_field=0;
  result->handle=	mysql;
  result->current_row=	0;
  mysql->fields=0;			/* fields is now in result */
  clear_alloc_root(&mysql->field_alloc);
  mysql->status=MYSQL_STATUS_USE_RESULT;
  mysql->unbuffered_fetch_owner= &result->unbuffered_fetch_cancelled;
  DBUG_RETURN(result);			/* Data is read to be fetched */


HANDLE create_shared_memory(MYSQL *mysql,NET *net, uint connect_timeout)
{
  ulong smem_buffer_length = shared_memory_buffer_length + 4;
  /*
    event_connect_request is event object for start connection actions
    event_connect_answer is event object for confirm, that server put data
    handle_connect_file_map is file-mapping object, use for create shared
    memory
    handle_connect_map is pointer on shared memory
    handle_map is pointer on shared memory for client
    event_server_wrote,
    event_server_read,
    event_client_wrote,
    event_client_read are events for transfer data between server and client
    handle_file_map is file-mapping object, use for create shared memory
  */
  HANDLE event_connect_request = NULL;
  HANDLE event_connect_answer = NULL;
  HANDLE handle_connect_file_map = NULL;
  char *handle_connect_map = NULL;

  char *handle_map = NULL;
  HANDLE event_server_wrote = NULL;
  HANDLE event_server_read = NULL;
  HANDLE event_client_wrote = NULL;
  HANDLE event_client_read = NULL;
  HANDLE event_conn_closed = NULL;
  HANDLE handle_file_map = NULL;
  ulong connect_number;
  char connect_number_char[22], *p;
  char *tmp= NULL;
  char *suffix_pos;
  DWORD error_allow = 0;
  DWORD error_code = 0;
  DWORD event_access_rights= SYNCHRONIZE | EVENT_MODIFY_STATE;
  char *shared_memory_base_name = mysql->options.shared_memory_base_name;
  static const char *name_prefixes[] = {"","Global\\"};
  const char *prefix;
  int i;

  /*
    If this is NULL, somebody freed the MYSQL* options.  mysql_close()
    is a good candidate.  We don't just silently (re)set it to
    def_shared_memory_base_name as that would create really confusing/buggy
    behavior if the user passed in a different name on the command-line or
    in a my.cnf.
  */
  DBUG_ASSERT(shared_memory_base_name != NULL);

  /*
     get enough space base-name + '_' + longest suffix we might ever send
   */
  if (!(tmp= (char *)my_malloc(strlen(shared_memory_base_name) + 32L, MYF(MY_FAE))))
    goto err;

  /*
    The name of event and file-mapping events create agree next rule:
    shared_memory_base_name+unique_part
    Where:
    shared_memory_base_name is unique value for each server
    unique_part is uniquel value for each object (events and file-mapping)
  */
  for (i = 0; i< array_elements(name_prefixes); i++)
  {
    prefix= name_prefixes[i];
    suffix_pos = strxmov(tmp, prefix , shared_memory_base_name, "_", NullS);
    strmov(suffix_pos, "CONNECT_REQUEST");
    event_connect_request= OpenEvent(event_access_rights, FALSE, tmp);
    if (event_connect_request)
    {
      break;
    }
  }
  if (!event_connect_request)
  {
    error_allow = CR_SHARED_MEMORY_CONNECT_REQUEST_ERROR;
    goto err;
  }
  strmov(suffix_pos, "CONNECT_ANSWER");
  if (!(event_connect_answer= OpenEvent(event_access_rights,FALSE,tmp)))
  {
    error_allow = CR_SHARED_MEMORY_CONNECT_ANSWER_ERROR;
    goto err;
  }
  strmov(suffix_pos, "CONNECT_DATA");
  if (!(handle_connect_file_map= OpenFileMapping(FILE_MAP_WRITE,FALSE,tmp)))
  {
    error_allow = CR_SHARED_MEMORY_CONNECT_FILE_MAP_ERROR;
    goto err;
  }
  if (!(handle_connect_map= MapViewOfFile(handle_connect_file_map,
					  FILE_MAP_WRITE,0,0,sizeof(DWORD))))
  {
    error_allow = CR_SHARED_MEMORY_CONNECT_MAP_ERROR;
    goto err;
  }

  /* Send to server request of connection */
  if (!SetEvent(event_connect_request))
  {
    error_allow = CR_SHARED_MEMORY_CONNECT_SET_ERROR;
    goto err;
  }

  /* Wait of answer from server */
  if (WaitForSingleObject(event_connect_answer,connect_timeout*1000) !=
      WAIT_OBJECT_0)
  {
    error_allow = CR_SHARED_MEMORY_CONNECT_ABANDONED_ERROR;
    goto err;
  }

  /* Get number of connection */
  connect_number = uint4korr(handle_connect_map);/*WAX2*/
  p= int10_to_str(connect_number, connect_number_char, 10);

  /*
    The name of event and file-mapping events create agree next rule:
    shared_memory_base_name+unique_part+number_of_connection

    Where:
    shared_memory_base_name is uniquel value for each server
    unique_part is uniquel value for each object (events and file-mapping)
    number_of_connection is number of connection between server and client
  */
  suffix_pos = strxmov(tmp, prefix , shared_memory_base_name, "_", connect_number_char,
		       "_", NullS);
  strmov(suffix_pos, "DATA");
  if ((handle_file_map = OpenFileMapping(FILE_MAP_WRITE,FALSE,tmp)) == NULL)
  {
    error_allow = CR_SHARED_MEMORY_FILE_MAP_ERROR;
    goto err2;
  }
  if ((handle_map = MapViewOfFile(handle_file_map,FILE_MAP_WRITE,0,0,
				  smem_buffer_length)) == NULL)
  {
    error_allow = CR_SHARED_MEMORY_MAP_ERROR;
    goto err2;
  }

  strmov(suffix_pos, "SERVER_WROTE");
  if ((event_server_wrote = OpenEvent(event_access_rights,FALSE,tmp)) == NULL)
  {
    error_allow = CR_SHARED_MEMORY_EVENT_ERROR;
    goto err2;
  }

  strmov(suffix_pos, "SERVER_READ");
  if ((event_server_read = OpenEvent(event_access_rights,FALSE,tmp)) == NULL)
  {
    error_allow = CR_SHARED_MEMORY_EVENT_ERROR;
    goto err2;
  }

  strmov(suffix_pos, "CLIENT_WROTE");
  if ((event_client_wrote = OpenEvent(event_access_rights,FALSE,tmp)) == NULL)
  {
    error_allow = CR_SHARED_MEMORY_EVENT_ERROR;
    goto err2;
  }

  strmov(suffix_pos, "CLIENT_READ");
  if ((event_client_read = OpenEvent(event_access_rights,FALSE,tmp)) == NULL)
  {
    error_allow = CR_SHARED_MEMORY_EVENT_ERROR;
    goto err2;
  }

  strmov(suffix_pos, "CONNECTION_CLOSED");
  if ((event_conn_closed = OpenEvent(event_access_rights,FALSE,tmp)) == NULL)
  {
    error_allow = CR_SHARED_MEMORY_EVENT_ERROR;
    goto err2;
  }
  /*
    Set event that server should send data
  */
  SetEvent(event_server_read);

err2:
  if (error_allow == 0)
  {
    net->vio= vio_new_win32shared_memory(handle_file_map,handle_map,
                                         event_server_wrote,
                                         event_server_read,event_client_wrote,
                                         event_client_read,event_conn_closed);
  }
  else
  {
    error_code = GetLastError();
    if (event_server_read)
      CloseHandle(event_server_read);
    if (event_server_wrote)
      CloseHandle(event_server_wrote);
    if (event_client_read)
      CloseHandle(event_client_read);
    if (event_client_wrote)
      CloseHandle(event_client_wrote);
    if (event_conn_closed)
      CloseHandle(event_conn_closed);
    if (handle_map)
      UnmapViewOfFile(handle_map);
    if (handle_file_map)
      CloseHandle(handle_file_map);
  }
err:
  my_free(tmp);
  if (error_allow)
    error_code = GetLastError();
  if (event_connect_request)
    CloseHandle(event_connect_request);
  if (event_connect_answer)
    CloseHandle(event_connect_answer);
  if (handle_connect_map)
    UnmapViewOfFile(handle_connect_map);
  if (handle_connect_file_map)
    CloseHandle(handle_connect_file_map);
  if (error_allow)
  {
    if (error_allow == CR_SHARED_MEMORY_EVENT_ERROR)
      set_mysql_extended_error(mysql, error_allow, unknown_sqlstate,
                               ER(error_allow), suffix_pos, error_code);
    else
      set_mysql_extended_error(mysql, error_allow, unknown_sqlstate,
                               ER(error_allow), error_code);
    return(INVALID_HANDLE_VALUE);
  }
  return(handle_map);
}


cli_advanced_command(MYSQL *mysql, enum enum_server_command command,
		     const uchar *header, ulong header_length,
		     const uchar *arg, ulong arg_length, my_bool skip_check,
                     MYSQL_STMT *stmt)
{
  NET *net= &mysql->net;
  my_bool result= 1;
  my_bool stmt_skip= stmt ? stmt->state != MYSQL_STMT_INIT_DONE : FALSE;
  DBUG_ENTER("cli_advanced_command");

  if (mysql->net.vio == 0)
  {						/* Do reconnect if possible */
    if (mysql_reconnect(mysql) || stmt_skip)
      DBUG_RETURN(1);
  }
  if (mysql->status != MYSQL_STATUS_READY ||
      mysql->server_status & SERVER_MORE_RESULTS_EXISTS)
  {
    DBUG_PRINT("error",("state: %d", mysql->status));
    set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate);
    DBUG_RETURN(1);
  }

  net_clear_error(net);
  mysql->info=0;
  mysql->affected_rows= ~(my_ulonglong) 0;
  /*
    We don't want to clear the protocol buffer on COM_QUIT, because if
    the previous command was a shutdown command, we may have the
    response for the COM_QUIT already in the communication buffer
  */
  net_clear(&mysql->net, (command != COM_QUIT));

  if (net_write_command(net,(uchar) command, header, header_length,
			arg, arg_length))
  {
    DBUG_PRINT("error",("Can't send command to server. Error: %d",
			socket_errno));
    if (net->last_errno == ER_NET_PACKET_TOO_LARGE)
    {
      set_mysql_error(mysql, CR_NET_PACKET_TOO_LARGE, unknown_sqlstate);
      goto end;
    }
    end_server(mysql);
    if (mysql_reconnect(mysql) || stmt_skip)
      goto end;
    if (net_write_command(net,(uchar) command, header, header_length,
			  arg, arg_length))
    {
      set_mysql_error(mysql, CR_SERVER_GONE_ERROR, unknown_sqlstate);
      goto end;
    }
  }
  result=0;
  if (!skip_check)
    result= ((mysql->packet_length=cli_safe_read(mysql)) == packet_error ?
	     1 : 0);
end:
  DBUG_PRINT("exit",("result: %d", result));
  DBUG_RETURN(result);
}


SSL_SESSION* SSL_get1_session(SSL* ssl)  /* what's ref count */
{
    return SSL_get_session(ssl);
}


void SSL::SetQuietShutdown(bool mode)
{
  quietShutdown_ = mode;
}


Buffers::~Buffers()
{
    STL::for_each(handShakeList_.begin(), handShakeList_.end(),
                  del_ptr_zero()) ;
    STL::for_each(dataList_.begin(), dataList_.end(),
                  del_ptr_zero()) ;
    ysDelete(rawInput_);
    ysDelete(output_);
}


int SSL_CTX_use_PrivateKey_file(SSL_CTX* ctx, const char* file, int format)
{
    return read_file(ctx, file, format, PrivateKey);
}


x509& x509::operator=(const x509& that)
{
    x509 temp(that);
    Swap(temp);
    return *this;
}


void SSL::addBuffer(output_buffer* b)
{
    buffers_.useHandShake().push_back(b);
}


void SSL::order_error()
{
    SetError(out_of_order);
}


const x509* SSL_CTX::getKey() const
{
    return privateKey_;
}


ASN1_TIME* X509_get_notAfter(X509* x)
{
    if (x) return x->GetAfter();
    return 0;
}


PublicKey::PublicKey(const byte* k, word32 s) : key_(0), sz_(0)
{
    if (s) {
        SetSize(s);
        SetKey(k);
    }
}


x509::x509(const x509& that) : length_(that.length_),
                               buffer_(NEW_YS opaque[length_])
{
    memcpy(buffer_, that.buffer_, length_);
}


const ConnectState& States::GetConnect() const
{
    return connectState_;
}


void SSL_SESSION::SetTimeOut(uint t)
{
    timeout_ = t;
}


uint CertManager::get_peerKeyLength() const
{
    return peerPublicKey_.get_size();
}


void SSL::verifyState(const RecordLayerHeader& rlHeader)
{
    if (GetError()) return;

    if (rlHeader.version_.major_ != 3 || rlHeader.version_.minor_ > 2) {
        SetError(badVersion_error);
        return;
    }

    if (states_.getRecord() == recordNotReady ||
            (rlHeader.type_ == application_data &&        // data and handshake
             states_.getHandShake() != handShakeReady) )  // isn't complete yet
              SetError(record_layer);
}


void DH_free(DH* dh)
{
    ysDelete(dh->g);
    ysDelete(dh->p);
    ysDelete(dh);
}


long SSL_CTX_set_options(SSL_CTX*, long)
{
    // TDOD:
    return SSL_SUCCESS;
}


void SSL::PeekData(Data& data)
{
    if (GetError()) return;
    uint   dataSz   = data.get_length();        // input, data size to fill
    size_t elements = buffers_.getData().size();

    data.set_length(0);                         // output, actual data filled
    dataSz = min(dataSz, bufferedData());

    Buffers::inputList::iterator front = buffers_.useData().begin();

    while (elements) {
        uint frontSz = (*front)->get_remaining();
        uint readSz  = min(dataSz - data.get_length(), frontSz);
        uint before  = (*front)->get_current();

        (*front)->read(data.set_buffer() + data.get_length(), readSz);
        data.set_length(data.get_length() + readSz);
        (*front)->set_current(before);

        if (data.get_length() == dataSz)
            break;

        elements--;
        front++;
    }
}


SSL_SESSION::SSL_SESSION(RandomPool& ran)
    : bornOn_(0), timeout_(0), random_(ran), peerX509_(0)
{
    memset(sessionID_, 0, ID_LEN);
    memset(master_secret_, 0, SECRET_LEN);
    memset(suite_, 0, SUITE_LEN);
}


void DH_Decoder::ReadHeader()
{
    GetSequence();
}


const opaque* SSL_SESSION::GetID() const
{
    return sessionID_;
}


word32 EncodeDSA_Signature(const byte* signature, byte* output)
{
    Integer r(signature, 20);
    Integer s(signature + 20, 20);

    return EncodeDSA_Signature(r, s, output);
}


    int Compress(const byte* in, int sz, input_buffer& buffer)
    {
        byte     tmp[LENGTH_SZ];
        z_stream c_stream; /* compression stream */

        buffer.allocate(sz + sizeof(uint16) + COMPRESS_EXTRA);

        c_stream.zalloc = myAlloc;
        c_stream.zfree  = myFree;
        c_stream.opaque = (voidpf)0;

        c_stream.next_in   = const_cast<byte*>(in);
        c_stream.avail_in  = sz;
        c_stream.next_out  = buffer.get_buffer() + sizeof(tmp);
        c_stream.avail_out = buffer.get_capacity() - sizeof(tmp);

        if (deflateInit(&c_stream, 8) != Z_OK) return -1;
        int err = deflate(&c_stream, Z_FINISH);
        deflateEnd(&c_stream);
        if (err != Z_OK && err != Z_STREAM_END) return -1;

        c16toa(sz, tmp);
        memcpy(buffer.get_buffer(), tmp, sizeof(tmp));
        buffer.add_size(c_stream.total_out + sizeof(tmp));

        return 0;
    }


void SSL::SetError(YasslError ye)
{
    states_.SetError(ye);
    //strncpy(states_.useString(), e.what(), mySTL::named_exception::NAME_SIZE);
    // TODO: add string here
}


void SSL::fillData(Data& data)
{
    if (GetError()) return;
    uint dataSz   = data.get_length();        // input, data size to fill
    size_t elements = buffers_.getData().size();

    data.set_length(0);                         // output, actual data filled
    dataSz = min(dataSz, bufferedData());

    for (size_t i = 0; i < elements; i++) {
        input_buffer* front = buffers_.getData().front();
        uint frontSz = front->get_remaining();
        uint readSz  = min(dataSz - data.get_length(), frontSz);

        front->read(data.set_buffer() + data.get_length(), readSz);
        data.set_length(data.get_length() + readSz);

        if (readSz == frontSz) {
            buffers_.useData().pop_front();
            ysDelete(front);
        }
        if (data.get_length() == dataSz)
            break;
    }

    if (buffers_.getData().size() == 0) has_data_ = false;  // none left
}


void SSL::set_masterSecret(const opaque* sec)
{
    memcpy(secure_.use_connection().master_secret_, sec, SECRET_LEN);
}


void SSL::Send(const byte* buffer, uint sz)
{
    unsigned int sent = 0;

    if (socket_.send(buffer, sz, sent) != sz) {
        if (socket_.WouldBlock()) {
            buffers_.SetOutput(NEW_YS output_buffer(sz - sent, buffer + sent,
                                                    sz - sent));
            SetError(YasslError(SSL_ERROR_WANT_WRITE));
        }
        else
            SetError(send_error);
    }
}


int SSL_read(SSL* ssl, void* buffer, int sz)
{
    Data data(min(sz, MAX_RECORD_SIZE), static_cast<opaque*>(buffer));
    return receiveData(*ssl, data);
}


long SSL_CTX_sess_number(SSL_CTX* ctx)
{
    return ctx->GetStats().number_;
}


int SSL_CTX_use_certificate_file(SSL_CTX* ctx, const char* file, int format)
{
    return read_file(ctx, file, format, Cert);
}


word32 CertDecoder::GetSignature()
{
    if (source_.GetError().What()) return 0;
    byte b = source_.next();

    if (b != BIT_STRING) {
        source_.SetError(BIT_STR_E);
        return 0;
    }

    sigLength_ = GetLength(source_);
    if (sigLength_ <= 1 || source_.IsLeft(sigLength_) == false) {
        source_.SetError(CONTENT_E);
        return 0;
    }

    b = source_.next();
    if (b != 0) {
        source_.SetError(EXPECT_0_E);
        return 0;
    }
    sigLength_--;

    signature_ = NEW_TC byte[sigLength_];
    memcpy(signature_, source_.get_current(), sigLength_);
    source_.advance(sigLength_);

    return sigLength_;
}