The Zephyr Protocol
Greg Hudson
2000-02-17

Protocol entities
-----------------

Three different kinds of entities participate in the zephyr protocol:
client programs, host managers, and servers.

Zephyr is a UDP protocol.  All Zephyr communications use the same
packet format, whether for displayable messages or for control notices
internal to the protocol.

Displayable messages generally originate from client programs (except
for login notices, which originate from servers), are fragmented into
multiple packets if necessary, and are sent to the host manager, which
forwards them to the server.  (The server will reject displayable
notices if it receives them from a source port other than the host
manager port, 2104.)  The server then sends the notice directly to the
receiving client programs, without involving the host manager on the
receiving end.

Control messages may originate from any party.  If a client program is
sending a control message to its host's server, it sends the packet to
the host manager unless it is a status request.  (The server will
reject control messages other than status requests if they do not come
from the host manager port.)  The host manager will forward the
control message to the server, which will send its acknowledgement, if
any, back to the host manager.

Packet format
-------------

A zephyr packet consists of a header and a body.  A header the
concatenation of a sequence of zero-terminated fields.  Fields come in
the following order:

	version
	numfields
	kind
	uid
	port
	authstatus
	authlen
	authenticator
	class
	instance
	opcode
	sender
	recipient
	defaultformat
	checksum
	multipart
	multiuid
	<other fields>

The message body (which may be fragmented across multiple packets)
also consists of a sequence of zero-terminated fields.  The meaning of
those fields is defined by zwgc.desc convention; for a conversational
message, the fields are typically "signature" and "body"; for a login
notice, the fields are typically "host", "when", and "tty".

Some header fields encode binary data in a format called
"zephyrascii".  The zephyrascii encoding of series of bytes is "0x"
followed by two uppercase hexadecimal characters for each byte (the
first character for the most significant four bits, the second
character for the least significant four bits).  For instance, the
zephyrascii encoding of the byte sequence { 166, 35, 6 } would be
"0xA62306".  The zephyrascii encoding of a 16-bit or 32-bit numeric
value is "0x" followed by an uppercase hexadecimal character for each
four bits of the value, starting with the most significant.  For
example, the 32-bit value 1 would be encoded as "0x00000001".

Following are descriptions of the named header fields:

version		The string "ZEPH0.2", giving the major and minor
		number of the packet.  The current implementation will
		fail to interoperate if the major version is not 0,
		and it ignores the minor version.

numfields	A 4-byte zephyrascii quantity giving the number of
		header fields.  Typically 17.  A numfields value
		greater than 17 will trigger a bug in sufficiently old
		Athena client code (prior to release 8.2).

kind		A 4-byte zephyrascii quantity telling the host manager
		how to handle a packet.  Defined values are:

		Value	Mnemonic	Description
		-----	--------	-----------
		0	UNSAFE		Client wants no acks
		1	UNACKED		Client want just HM ack
		2	ACKED		Client wants HM+server ack
		3	HMACK		Host manager ack
		4	HMCTL		Host manager control
		5	SERVACK		Server ack
		6	SERVNAK		Server negative ack
		7	CLIENTACK	Client ack
		8	STAT		HM status request

		(Mnemonics are provided for the purpose of this
		document and have nothing to do with the wire
		protocol.)

		Note that the host manager always receives an ack from
		the server for packets it forwards, even if the packet
		kind is UNACKED or UNSAFE.  Those kind values just
		tell the host manager which acks to send to the client
		program.

uid		A 12-byte zephyrascii structure consisting of:

		4 bytes:	Client interface IP address, in
				network byte order
		8 bytes:	A per-client nonce (typically the
				result of gettimeofday() treated as a
				raw 64-bit value)

		zwgc abuses the IP address part of the unique ID,
		using it as the originating IP address of the message.
		The zephyr server enforces that the IP address matches
		the IP source address of the packet it received.

		Acknowledgements contain the same uid as the packet
		being acknowledged.

port		A 2-byte zephyrascii quantity used by some types of
		server control notices.

authstatus	A 4-byte zephyrascii quantity, with value 1 if the
		packet is authenticated or 0 if not.

authlen		A 4-byte zephyrascii quantity giving the length of the
		raw krb4 authenticator.  If an entity other than a
		client is creating the packet, this field will be set
		to 0.

authenticator	The zephyrascii-encoded krb4 authenticator.  If an
		entity other than a client is creating the packet,
		this field will be set to "0x" (the zephyrascii
		encoding of the empty string).

class		The notice class.

instance	The notice instance.

opcode		The notice opcode, used for server control messages.

sender		The notice sender.

recipient	The notice recipient (with Kerberos realm).

defaultformat	The notice default format (set by the sender and used
		by zwgc; the host manager and server don't peek at
		it).

checksum	A 4-byte zephyrascii quantty giving the checksum of
		the packet.  This field has to be filled in last.
		Clients compute the checksum by taking the xor of the
		des_quad_cksum() values of the three following byte
		ranges:

			Header bytes before the checksum field
			Header bytes after the checksum field
			Message body

		where each checksum uses the credential session key as
		a cryptographic seed.  Servers only compute the
		checksum on the header bytes before the checksum
		field.

multipart	Used for fragmenting message bodies across packets.  A
		string "X/Y" where X is a decimal number giving the
		offset of this fragment within the message body (0 for
		the first fragment) and Y is a decimal number giving
		the total length of the message body.  Unfragmented
		notices should fill in this field (with "0/len" where
		len is the length of the message body).  Note that
		fragmentation is a client-to-client process; the host
		manager and server just forward fragments around.

multiuid	The uid of the first fragment of a message.  (For
		unfragmented messages, this has the same value as the
		uid field.)  Also not used by the server or host
		manager.

<other fields>	Other fields may be added to the header by increasing
		numfields beyond 17 (but see the note about
		compatibility with buggy clients).

Host manager control notices
----------------------------

If the host manager receives a notice from the loopback address of
kind STAT, it will respond with a notice of kind HMACK and with a
message body containing the following zero-terminated fields (using
ASCII decimal format for numbers):

	Current server name
	Queue length
	Client packets received
	Server packets received
	Number of server changes since start
	RCS ID of zhm source file
	Whether zhm is looking for a new server ("yes" or "no")
	Number of seconds since start
	Heap size (well, it used to be; these days just "-1")
	Machine type

If the host manager receives a notice from the loopback address of
kind HMCTL, it examines the opcode field.  zhm understands the
following client control opcodes:

	"FLUSH"		zhm sends a flush notice to its server (see
			"Server control notices" below), causing the
			servers to flush all data about the host.  zhm
			will send a boot notice to its server just
			before forwarding the next packet.

	"NEWSERV"	zhm switches to a different server.

If the host manager receives a notice from a non-loopback address of
kind HMCTL, it examines the opcode field.  zhm understands the
following server control opcodes:

	"SHUTDOWN"	zhm switches to a new server.  If the message
			body contains an IP address which
			reverse-resolves, zhm will switch to that
			server if it is in zhm's list of servers.

	"PING"		zhm sends the packet back to the server, with
			the kind changed to HMACK.

Server control notices
----------------------

Unless noted otherwise, control messages will be forwarded from the
server which originally received the message to the other servers.

Unless noted otherwise, control messages are acknowledged by the
server.  Positive acknowledgements to control messages all follow the
same format: the message being acknowledged is returned to the client
(through the host manager) with the kind set to SERVACK and the
message body sent to the single 0-terminated field "SENT".  Negative
acknowledgements may have the kind set to SERVNAK and the message body
set to one of:

	"FAIL" (SERVNAK)	User not found (for uloc request)
	"LOST" (SERVNAK)	Authorization failed
	"LOST" (SERVACK)	Some other failure

If the server receives a packet of kind CLIENTACK, it treats it as an
acknowledgement of the packet it sent with the CLIENTACK packet's uid.

If the server receives a packet of class "HM_CTL" or a packet of class
"ZEPHYR_CTL" and instance "HM", it expects the packet kind to be
HMCTL, and examines the opcode field.  zephyrd understands the
following host manager control opcodes:

	"FLUSH"		zephyrd flushes all information about the
			host (determined by the source address of the
			packet).  This message is not acknowledged.

	"BOOT"		zephyrd flushes all information about the
			host.  The server which originally received
			the message will add the host to its list
			(which is only used to decide who to send
			shutdown messages to when the server process
			is killed).

	"ATTACH"	The server which originally received the
			message will add the host to its list.  Other
			servers will remove it from their list.

	"DETACH"	The server will remove the host from its
			list.  This message is not forwarded to other
			servers and is not acknowledged.

If the server receives a packet of class "ZEPHYR_CTL" and an instance
other than "HM", it examines the opcode.  zephyrd understands the
following control opcodes:

	"GIMME"		The server will acknowledge the request and
			send directly to the client program (using the
			port field of the request) a list of currently
			active subscriptions for the client.  The
			response is not authenticated and may be
			fragmented.  The message body consists of
			three fields for each subscription, one each
			for the class, instance, and recipient of the
			subscription triplet.

	"GIMMEDEFS"	In the same fashion as "GIMME", the server
			will respond with a list of the default
			subscriptions it is configured with.

	"SUBSCRIBE"	The message body of the request (which must
			fit in one fragment) contains three fields for
			each subscription.  The server will add
			all of the subscriptions to the client's
			subscription list, plus the default
			subscriptions the server was configured with.

	"SUBSCRIBE_NODEFS"	Like "SUBSCRIBE", but without the
				default subscriptions.

	"UNSUBSCRIBE"	Like "SUBSCRIBE_NODEFS", but subscriptions are
			removed from the client's list instead of
			added.

	"CLEARSUB"	All of the subscriptions for the client are
			removed and the location entry for the client
			is cleared.

[XXX unfinished beyond this point]

If the server receives a packet of class "LOGIN",

	"USER_LOGOUT"
	"USER_FLUSH"
	"NONE"
	"OPSTAFF"
	"REALM-VISIBLE"
	"REALM-ANNOUNCED"
	"NET-VISIBLE"
	"NET-ANNOUNCED"

If the server receives a packet of class "USER_LOCATE",

	"LOCATE"

If the server receives a packet of class "ZEPHYR_ADMIN",

	"STATUS"