MIT Information Systems

Macintosh Development

[Home] [About Us] [People] [Information Systems]
[Kerberos for Macintosh] [Applications] [Miscellaneous Documentation]


TechNotify Protocol Documentation
An Overview

The TechNotify protocol is a lightweight protocol designed to allow clients to frequently query for new mail with as little impact on the mail server as possible.

In order to accomplish this goal, the authors of the protocol made three important design decisions:

  1. The protocol uses UDP rather than TCP.

    Because the TechNotify protocol only needs simple request and reply packets, a connection oriented protocol like TCP has a lot of unnecessary overhead. More importantly, TCP sockets use a kernel file descriptor for each connection, and there are often more mail spools on a post office server than available file descriptors. Thus if most of the users were using TechNotify, the post office server could run out of file descriptors (this is Very Bad).

    TechNotify uses UDP port 3713

    .
  2. Authentication is cached using a Kerberos v4 session key.

    By caching authentication, the TechNotify server avoids the Kerberos v4 authentication overhead on every query (since Kerberos authentication requires contacting a remote kerberos server, this can be expensive). Most TechNotify clients poll for new mail at least once an hour (usually more like once every 1 to 5 minutes) and rarely only poll once, so the cache expiration can be relatively aggressive and still achieve a noticeable performance improvement.

  3. The server only returns the size of the mail spool, not the number of messages, sender and subject information.

    By only returning the size of the mail spool, the server can just use fstat() on the spool file, rather than having sendmail lock and parse it. Locking and parsing the mail spool is what makes the POP stat command so expensive. In addition, losing the message count and subject line information actually has little impact on the users because TechNotify clients are normally used to automatically incorporate new mail. Thus they only need a boolean "Is there new mail?" response from the server.

As a result, the TechNotify protocol is very simple. The client and server negotiate a session key using an "authentication" request/reply pair. Then the client loops asking the server to check mail using a "check mail" request/reply pair. If the server replies that authentication has expired, the client renegotiates a new session key.

Request and Reply Data Formats

In order to keep the two request/reply data formats simple, similar structures are used in all of the request/reply pairs. Note that all numeric values are defined to be in network byte order.

The Request Header Structure

This data structure is the header for both the authentication request and the check mail request.

typedef struct {
  SInt32 version;
  SInt32 notifyOp;
  SInt32 length;
} RequestHeader;
version The version of the TechNotify protocol that the client is using. Currently only version 93 is supported.
notifyOp The type of request being made. Possible values are:
#define kAuthRequest      1     // Request to set up a session key
#define kCheckMailRequest 2     // Request to get the current size of the mail spool
#define kCloseRequest     3     // Request to flush authentication
length The size of appended data. If the request is an authentication request this is the size of the Kerberos v4 "rcmd" service ticket from which the session key will be negotiated. If the request is a check mail request, then length is sizeof(CheckMailRequestData). If the request is a close request, length is 0.

The CheckMailRequest Data Structure

This data structure is appended to the request header for check mail requests.

typedef struct {
  char   userName[12];
  UInt32 time;
  UInt32 checksum;
} CheckMailRequestData;
userName The name of the user whose mail spool you want to check. This should be the same as the kerberos user whose tickets were used to authenticate.
time The time at which the request was sent in Unix time (seconds since January 1, 1970). This is used to prevent replay attacks.
checksum The des_quad_chksum() of the negotiated session key. This is used to verify authentication.

The Reply Data Structure

This data structure is used by the server to reply to all the types of requests.

typedef struct {
  SInt32 error;
  SInt32 notifyOp;
  SInt32 size;
  UInt32 time;
} ReplyData;
error A result code (usually noErr). Possible values are:
#define kNoMailBoxError   2     // The user's mail spool is not on this post office server
#define kVerifyError      1000  // Authentication has expired
#define kAddressError     1001  // Local address does not match address in kerberos ticket
#define kServerError      1002  // Internal server failure (try again later)
#define kVersionError     1003  // Server does not support this version of the protocol
#define kBadPacketError   1004  // The server received a bad packet (try again)
notifyOp The server's reply. Possible replies are:
#define kAuthAck          4   // Authentication successfully negotiated
#define kAuthNak          5   // Authentication failed (see error)
#define kCheckMailAck     6   // Mail spool successfully examined
#define kCheckMailNak     7   // Unable to check mail spool (see error)
#define kCloseAck         8   // Authentication successfully flushed
#define kCloseNak         9   // Failed to flush cached authentication
size If type is kCheckMailAck, this contains the size if the user's mail spool. If size is 0, the user has no new mail waiting on the server.
time The time the request was sent.

A simplified view of a TechNotify client looks a lot like this:

FSM of a TechNotify client

The TechNotify client loops on an idle counter (where kInterval is most likely determined by a user preference). When it becomes time to check mail, the client checks authState. If it is already authenticated, it goes immediately to sending a check mail request. Otherwise, it gets Kerberos v4 tickets and an rcmd service ticket for the post office server. It then sends a RequestHeader with the service ticket appended to it and receives the reply.

When successfully authenticated, the client sends a RequestHeader with a CheckMailRequestData appended to it and waits for the reply. In failure conditions it reports an error and goes back to the idle state. If the server reports that the client is no longer authenticated, the client goes into the authentication state.

Implementing a UDP based Protocol

The TechNotify protocol is written on top of UDP. Unlike TCP, UDP is connectionless and unreliable. This means that whenever you send data over a UDP socket, you must specify the recipient and handle the situations where your packet is lost or the response from the server is lost. Packets may also arrive garbled, so your parsing routines should fail gracefully.

The easiest way to implement request/reply pairs in UDP is with a Finite State Machine:

FSM of sending a UDP query and waiting for a response

Here we have basically a straight forward pipeline in which we send the request with sendto(), wait for the reply using select() and when it comes, receive it using recvfrom(). However, we have to create some loops to deal with the unreliable nature of UDP.

If select() fails to get anything after its timeout expires (and you should definitely not have an infinite timeout), then either the request was lost or the reply was lost. As a result we loop back and send the request again. Also, if the packet we receive from recvfrom() is bad, then the packet was garbled in transmission and we should send the request again.

Note that the total number of times which we can full retry is limited by the triesLeft variable. This is because you may never get a response if the server is broken or the network is especially bad. Rather than hanging forever, you application should give up and report a failure.


Questions or comments? Send mail to macdev@mit.edu
Last updated on $Date: 2003/11/18 21:59:31 $
Last modified by $Author: smcguire $