![]() |
Macintosh Development |
[Home]
[About Us]
[People]
[Information Systems]
[Kerberos for Macintosh]
[Applications]
[Miscellaneous Documentation]
An OverviewRequest and Reply Data FormatsThe 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:
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
.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.
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.
Implementing a UDP based ProtocolIn 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;
versionThe version of the TechNotify protocol that the client is using. Currently only version 93is supported.notifyOpThe 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 authenticationlengthThe 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 lengthissizeof(CheckMailRequestData). If the request is a close request,lengthis 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;
userNameThe 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. timeThe time at which the request was sent in Unix time (seconds since January 1, 1970). This is used to prevent replay attacks. checksumThe 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;
errorA 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)notifyOpThe 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 authenticationsizeIf typeiskCheckMailAck, this contains the size if the user's mail spool. Ifsizeis 0, the user has no new mail waiting on the server.timeThe time the request was sent. A simplified view of a TechNotify client looks a lot like this:
![]()
The TechNotify client loops on an idle counter (where
kIntervalis most likely determined by a user preference). When it becomes time to check mail, the client checksauthState. 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 aRequestHeaderwith the service ticket appended to it and receives the reply.
When successfully authenticated, the client sends a
RequestHeaderwith aCheckMailRequestDataappended 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.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:
![]()
Here we have basically a straight forward pipeline in which we send the request with
sendto(), wait for the reply usingselect()and when it comes, receive it usingrecvfrom(). 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 fromrecvfrom()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
triesLeftvariable. 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 $