Network Working Group Max Wildgrube INTERNET-DRAFT Category: Informational Document: draft-wildgrube-gnp-05.txt October 2003 Expiration Date: April 2004 SDXP: Structured Data Exchange Protocol This document is an Internet-Draft and is in full conformance with all provisions of Section 10 of RFC2026. Internet-Drafts are working documents of the Internet Engineering Task Force (IETF), its areas, and its working groups. Note that other groups may also distribute working documents as Internet-Drafts. Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress." The list of current Internet-Drafts can be accessed at http://www.ietf.org/ietf/1id-abstracts.txt The list of Internet-Draft Shadow Directories can be accessed at http://www.ietf.org/shadow.html. Please send your comments to the author: max@wildgrube.com ABSTRACT: This specification describes an all-purpose platform independent networking protocol for use to realize client/server (or similar) applications. User and administration messages are transformed into a special network format (see [SDXF]). This format is self-describing and CPU-independent. Wildgrube Informational [Page 1] SDXP: Structured Data Exchange Protocol October 2003 CONTENTS: 1. Introduction 2. General Message Format 3. User Messages (Payload) 4. Establishment of connection (Handshaking) 5. Message Digest 6. Administrator Messages 7. Security Considerations 8. New-Password protocol 9. IANA considerations Appendix. Example: Realizing a RPC protocol References Author's Address 1. Introduction This proposal describes a multi-purpose networking protocol. In the following it is named "SDXP" (Structured Data Exchange Protocol). Is this paper a proposal for just another Internet client/server protocol? What is the benefit of SDXP? SDXP is _not_ another networking protocol like TCP/IP, ESRO (see [ESRO]) or VMTP (see [VMTP]), it is a protocol level between the transport layer and the application layer. It may be regarded as a generalized kind of a RPC protocol. If somewhere wants to design such an application protocol he (or she of course!) has to realize two important tasks: 1. He had to decide if the service needs authentication or not; and if needed he had to define an authentication (handshaking) protocol. On the server side he has to realize user administration functions. 2. He had to think about how the reply and response messages are packed to be well suited to be send over the net. May be he has taken into account that different computer architectures are involved (endianess, character representation). In relation to these two points every new protocol reinvents every time the wheels again. More or less: If no authentication is needed and we are playing in a unique environment and there is no complex data structures involved, there may be no need for a specific protocol layer. For the most kind of RPC protocols both of these points are taken into account, especially if such a protocol demands an universal usage. Wildgrube Informational [Page 2] SDXP: Structured Data Exchange Protocol October 2003 If you compare SDXP with the RPC protocol of [SUN] you will see that the overwhelming part of [SUN] is also covered by SDXP. The rest to be done belongs to the application layer: He had to define the request (call) and reply messages in terms of SDXF chunks. As an example of a practical realization of SDXP this is outlined in Appendix A. This protocol describes several levels of operations. It may be helpful to assign these levels to the OSI 7-Layer model: +-----+--------------------+-----------------------+ |Level| Layer | assigned to: | +-----+--------------------+-----------------------+ | 7 | Application Layer | application | | 6 | Presentation Layer | SDXF 1) | | 5 | Session Layer | SDXP | | 4 | Transport Layer | p.e. TCP or VMTP 2) | | 3 | Network Layer | p.e. IP 2) | | 2 | Data Link Layer | | | 1 | Physical Layer | | +-----+--------------------+-----------------------+ Hints: 1. SDXF stands for "Structured Data eXchange Format" and is described in [SDXF]. For full understanding of this paper it is recommended to read [SDXF] first. SDXF is chosen as the underlying message format, because it is self-describing and platform independent. There are already some standards for Internet data exchanging, IETF prefers ASN.1 and XML therefore. So the reasons for establish an other data format should be discussed. Some basic discussion is done in [SDXF]. Why is this proposal divided between two concepts, SDXF and SDXP? Most other protocol proposals defines his data structures as a part of itself. But this subdivision is a significant concept of this proposal, because SDXF plays a dual role: At first it is the underlying data format for the SDXP itself, at second it is a frame for the messages which are to be defined in the Application Layer. With this the programmer which realizes an application based on SDXP has automatically access to the SDXF functions to maintain the application defined messages. The application programmer is _not_ involved with the SDXP protocol (only with the API of a given implementation of SDXP), but he is involved in the handling of the messages and therefore in SDXF. Wildgrube Informational [Page 3] SDXP: Structured Data Exchange Protocol October 2003 2. Even this proposal is independent of the Transport Layer, the terms of the TCP/IP is used, specially of the sockets interface. This should not be a important restriction: The great majority of today's networking are using this interface. Nevertheless, what we assume is: a. The communication is connect-oriented. b. The transport layer is responsible for a bidirectional, reliable, sequenced, and unduplicated flow of data. Record boundaries of messages must be preserved by the session layer. (If stream sockets are chosen the session layer is responsible to read the correct amount of data: it can do this because the header of the SDXF chunk of the message contains the data length and the length of the following message digest - if any - is fixed for a specific connection). 2. General Message Format Even we distinguish between three kind of messages - user (payload) messages, handshake messages and administrator messages - all of them has the same structure: A SDXP message consists of exactly one SDXF chunk, optionally followed by a message digest of fixed length. (The length of the message digest is fixed for a given connection.) After reading the SDXF header (the first 6 bytes of the message) the length of the complete message is known: The SDXF header contains the length of the following SDXF data (L), therefore the complete length is: L + 6 + length_of_digest. As a summary: The structure of all SDXP messages has following representation: +---+---+---+---+---+---+---+---+---+-...-+---+---+---+---+ |chunkID|fl | length | content | digest| +---+---+---+---+---+---+---+---+---+-...-+---+---+---+---+ (fl = SDXF flag byte) The chunk ID determines the type of message. It is a 2-byte number in network format, with it in big-endian representation, as described in [SDXF]. Wildgrube Informational [Page 4] SDXP: Structured Data Exchange Protocol October 2003 These 0-level chunk IDs are fixed in following manner: +----------+---------------+-------------------------+ | chunk ID | constant name | meaning | +----------+---------------+-------------------------+ | 32750 | ID_MESSAGE | user message | | 32751 | ID_CONNECT | connect message | | 32752 | ID_DISCONNECT | disconnect message | | 32753 | ID_ADMIN | administration message | | 32754 | ID_AUTH | authentication message | | 32755 | ID_HSHAKE | handshake message | +----------+---------------+-------------------------+ The flag byte determines the type of the SDXF content part. In most cases the SDXF chunk is "structured" that is: the content consists itself of a list of SDXF chunks. In the following we use a graphical tree notation for the SDXF chunks: (The chunk ID are noted without the prefix "ID_") A message which consists of a single elementary chunk: (MESSAGE) Hint: This notation is a short form for the sequence ID_MESSAGE - tag - length - data A message which consists of a structured chunk containing chunks with chunk IDs ID_ABC and ID_XYZ: (MESSAGE) | +--(ABC) +--(XYZ) A more complex message, the sub-chunk CMD is itself structured: (such complex messages are not used in the Session Layer, there are only used by user messages, see Appendix) (ADMIN) | +--(USERID) +--(CMD) | | | +--(PAR1) | +--(PAR2) | +--(PAR3) | +--(CONNID) +--(BUFFSIZE) Wildgrube Informational [Page 5] SDXP: Structured Data Exchange Protocol October 2003 If we want to note also the type of a chunk, we use the notation (CHUNKID:x) with x = c for character, = n for numerical, = b for binary (bitstring) chunks. The highest level of precision is to note the type and content of a chunk: (CHUNKID:x=content) with 'content' as a value or a name which describes the content of the chunk. Besides the described protocol specific chunks any arbitrary additional chunks are possible: An implementation which does not knows these extensions has to ignore them, following the basic rule in [SDXF] ("Ignore unknown chunks!") - see also chap. 9. Even we do not define implementation details in this proposal, we assume that there exists a data object of central meaning, called here "connection state block": For every connection a connection state block exists. A connection state block will be created after a connect() or accept() function call. At least a connection state block contains: - the socket number of the connection - the SDXF structure (or a pointer to it) for the message - authorization / encrypt token - a flag which marks partner as normal user or administrator - the length of the message digest (zero if none) - the actual Initialization Vector (IV) for the AES ciphering routines Depending on a given implementation a connection state block may include also a pointer to a partner specific data area (server), statistical data for accounting and information, partner specific information (user ID), etc. In an object oriented environment, this connection state block can realized as a class, in C++ you can distinguish between private and public entries. Wildgrube Informational [Page 6] SDXP: Structured Data Exchange Protocol October 2003 3. User Messages (Payload) The only rule for user messages is: the top-level chunk ID is ID_MESSAGE (32750). The chunk content is completely interpreted by the application. Or: the communication occurs completely in the Application Layer of the OSI model. In order to maintain the messages in the application layer the SDXF functions are to be used as described in [SDXF]. The message digest is transparent to the application, it is completely handled by the Session Layer (Level 5). But before user messages can be interchanged, a connection must be established. This is done by performing the "Handshake Protocol" which is described next. 4. The Handshake Protocol A connection must first be established on a physical level (TCP/IP socket functions: connect() and accept(). A logical connection communication follows by interchanging some messages between the two partners. This phase is called "handshaking". Despite the real role-playing of the partners: The partner which starts the communication with a connect() request is called "client", the partner which listen() for connect requests and accept() them is called "server". A server may accept (in a logical sense) a connection request or not. A server may reject a connection request if authorization is requested and checking user ID and password is failed. These are the steps of hand-shaking: H1. client requests connection to a server by executing the connect() function. H2. Server accepts the connect request and sends the handshake-request: (HSHAKE) | +--(TOKEN) +--(STATUS) +--(BUFFSIZE) +--(CONNID) +--(DIGEST) Wildgrube Informational [Page 7] SDXP: Structured Data Exchange Protocol October 2003 with: TOKEN type binary Optional, a (pseudo-) random generated 128-bit number (16 Byte) - it is used for authentication and encryption. STATUS type numeric Server status, is normally be READY (value=1), but may be QUIET (value=2) - see chap. 6. BUFFSIZE type numeric Maximal buffer size of server CONNID type binary Connect ID, an unequivocal 32-bit number to identify the connection, for information. DIGEST type numeric Optional, the length of the message digest, - see chap. 5 The message digests which are following each message (depending on ID_DIGEST) are not mentioned any more. H3. Client sends handshake-reply: (HSHAKE) | +--(USERID) +--(TOKEN) +--(ADMIN) +--(BUFFSIZE) with: USERID type char If applicable (authentication !). TOKEN type binary Password encoded token, if a token was sent (authentication !). ADMIN empty If client claims to be administrator. BUFFSIZE type numeric Minimum of clients and servers buffersize. Wildgrube Informational [Page 8] SDXP: Structured Data Exchange Protocol October 2003 H4. Server sends acknowledgment: (HSHAKE) | +--(ACK:n=1) +--(TOKEN:b=token) +--(DIGEST:n=length) or: (HSHAKE) | +--(ACK:n=0) and server disconnects by close()ing the socket. The role of "TOKEN" and "USERID" is explained in chap. 7, of "DIGEST" (optional) in chap. 5. The maximum buffer size used for payload messages is negotiated between client and server, the minimum of both is then used. A given implementation may create a connect message on the server - created as a result of a successful authorization check, not sended by the client. After receiving the positive acknowledge the client can send his requests to the server and receives the responses. All this messages has ID_MESSAGE as top level chunk ID, as described in chap. 3. An administrator can send administrator messages, which has ID_ADMIN as top level chunk ID (see chap. 6). If the client has finish he invoke a disconnect function call, which sends a disconnect message (DISCONNECT:b=0) to the server and then closes the socket handle. The values of the involved chunk IDs are listed in chap. 9. 5. Message Digest An implementation of SDXP may or may not support message digests, it is recommended to give the administrator of the server applications the option to switch on the support of digests. If digests are requested by the server (generally or for specific users), the handshake message H2 contains the ID_DIGEST chunk with the length of the digest as a numeric value. At this point the client's protocol stack does not know the length of a possible digest. After accepting the H2 message by the client - only reading the SDXF part at first, it recognizes that it contains an DIGEST chunk (or not), so it can read at next the following message digest. After Wildgrube Informational [Page 9] SDXP: Structured Data Exchange Protocol October 2003 handshaking, the phase of the payload messages begins. Of course also these payload messages has message digests - but: The digest trailers are transparent to the application (in the sense of level 7 of OSI 7-Layer model, see chap. 1). The digests are read and checked by the SDXP kernel functions. The application sees only the SDXF chunk. Moreover: The application does not know if digests are used or not (if we refrain from a specific error message when the digest check fails). 6. Administrator Messages A client can declare itself as an administrator. This must be done before it connects to the server to be administer (or as an option of the connect function call). If the server requests authorization, a special flag in the user table should be checked for to authorize an user as an administrator. The most important task for an administrator is to terminate a server in a proper way (instead of the operation system possibilities to terminate a program or task). An other task is to set the server to a QUIET state: In this state no further connections will be accepted (except for administrators of course), but the existing connections will continue on working. Additionally the connected clients could be informed of this state: The QUIET state may be used as a first step to terminate the server session in a user friendly way. Other administrator tasks are: - open and close the account file - get a list of connected users - statistical summaries. An administrator command consists of a small unstructured numerical chunk: (ADMIN:n) The value of this chunk acts as one of following predefined commands: ADMIN_stop = 1 terminates server immediately ADMIN_quiet = 2 sets server in QUIET status ADMIN_revive = 3 resets from QUIET status to READY again ADMIN_user = 4 requests a list of currently active users ADMIN_summary = 5 requests an activity statistic Wildgrube Informational [Page 10] SDXP: Structured Data Exchange Protocol October 2003 ADMIN_closeAcc = 6 closes the account file ADMIN_openAcc = 7 (re-)openes the account file ADMIN_reload = 8 reloads the user table Other commands may be implemented too. The commands with value > 1 will be only supported if applicable: ADMIN_quiet and _revive only if the server supports a QUIET status, ADMIN_user and _summary if the server supports information requests about active users and statistics, ADMIN_closeAcc and _openAcc if accounting is supported. ADMIN_reload is designed for servers which loads the complete table of authorized users into memory: If a new user is added into an external table (database), this command may be helpful for to activate this user while the server is running. After receiving and processing an administration command, and if otherwise no specific response is to be sent to the administrator client, server replies an acknowledgment: (ADMIN) | +--(ACK:n=x) with x=1 for a positive and x=0 for a negative acknowledgment. 7. Security Considerations For security reasons a specific installation of SDXP needs two security algorithms: 1. A hash algorithm to encrypt user passwords, security tokens and to generate message digests. 2. An encryption algorithm to encrypt the payload messages. For this actual version of SDXP [SHA-1] (see also [SCHNEIER], pg. 441) is chosen as the hash algorithm and [AES] (Rijndael) as the encryption algorithm. AES is a block ciphering algorithm. The SDXF encryption option is designed for length-preserving algorithms. So we use the Cipher Text Stealing (CTS) mode for block ciphers (described by [SCHNEIER] on pages 195 and 196). This mode handles any length of plaintext and produces cipher text whose length matches the plaintext length. The CTS mode behaves like the CBC mode for all but the last two blocks of the plaintext. This implies that at least two blocks are necessary for the CTS mode (the 2nd may be shorter than the block size). If the length of the whole message is shorter than the blocksize (or: if it consists of only one short block) a special mode ("short mode") is to be used: The Initial Vector (IV) is encrypted and the result is XORed with the plain text. This result in the length of the plain text is used Wildgrube Informational [Page 11] SDXP: Structured Data Exchange Protocol October 2003 as the ciphered text. There are three important points associated to security problems: a. The password may be spy out during authorization. b. The content of the messages may be spy out. c. The message may be distorted. To solve point (a) there is an important rule: Whenever possible we do not transfer a password through the network. Another rule is: As soon as possible an user-readable password is hashed (encrypted) with a special key. This key is unique for a given application; An "application" in this context is the set of all programs which has to communicate via SDXP. We call this key "application key". Important: Before hashing the password, it has to be translated into the "Network Character Set" using the same translation table as SDXF does - see [SDXF] chap. 4 (b): The plain text for hashing must be the same for client and server even for different platform architectures! In following the encryption of a user password, of the tokens and the generation of the message digest are all done by applying by the "keyed hashing algorithm": The key and the text to hash are concatenated to one data stream and on this result the hash algorithm [SHA-1] is applied. In the following we use the expression result := HASH (key + text) for this. We assume at first that the encrypted password is known on both side: On server side it is stored in an user table (or database: relation user ID / password), on client side the password is typed in at a login dialog (and password is encrypted immediately). The encrypted password is the same on both side (provided that the same application key is used). The encryption is done by: encrypted_password := HASH (application_key + password) After it accepts the connect request, the server generates a token which is unique for this session and connection (a random generated 128 bit value) and sends it with handshake message H2 (ID_TOKEN) to the client (see chap. 4). Client encrypts this token using his encrypted password as key: encrypted_token := HASH (encrypted_password + token) The encrypted token is sent back with H3 to the server (together with his user ID). The server itself looks in the user table (or data base) for an entry with this user ID, getting the associated Wildgrube Informational [Page 12] SDXP: Structured Data Exchange Protocol October 2003 encrypted password. With this password as key he encrypts for his part the original token. This encrypted token and the token sent back from client must match. If the encrypted tokens matches, server generates another token (token2) and sends it to the client as part of a positive acknowledgement (handshake message H4). Also with reference to the security problem (b): With this new token the same play starts again: both server and client encrypts this token with the known encrypted password. This encrypted token (token3) serves as an encryption and decryption key for to encrypt data chunks if desired. Inspite of this security sensitive role of token3, this token is never transmitted. token3 := HASH (encrypted_password + token2) The token sent by H2 (the first token) serves also as an "Initialization Vector" (IV) for the CBC mode of the AES cipher algorithm (Cipher Block Chaining Mode, see [SCHNEIER], page 194). To make every message unique (even if it contains the same text for some messages), this IV is modified before it is used for a new message. This modification is done by adding a 1 to every byte of the actual IV, ignoring an overflow (255 + 1 = 0). This modification has to be done synchronically on both sides. Till now, the password - even in his encrypted form - is never sent over the line. An exception of this rule is described in the next chapter: While transferring a new password, some additional security steps will be introduced. Nevertheless: Also this new password is never transferred in a readable form. Another aspect of security is intactness of data, but: While using TCP/IP (more precisely: IP) as a transmission medium we can trust on his CRC check on the transport layer. Point (c) addressed the use of message digests: besides the original message the encrypted password acts also as an input parameter for the digest algorithm: message_digest := HASH (encrypted_password + SDXF_message) Wildgrube Informational [Page 13] SDXP: Structured Data Exchange Protocol October 2003 8. New-Password protocol Many security plans (demanded by user or administration) requires a change of user passwords from time to time. An implementation of SDXP should provide a function to send a new user password to the server. This function has to be realized by following special protocol: (As a shortcut we use "enpassw" for "application key encrypted password".) At first the new password is transformed into the Network Character Set and encrypted by the client with the application key as described above: enpassw := HASH (application_key + new_password) A connection to the server is established. Then following message has to be send to the server: A1: (AUTH) | +--(PASS:b=enpassw) (again the message digests are not mentioned) The server tries to update the password of the user in the user table and reports the success via an acknowledge message: A2: (AUTH) | +--(ACK:n=x) with x=1 as a positive, x=0 as a negative acknowledgment. "Of course" these messages (A1 - A2) are encrypted: The encrypt flag of the level 0 SDXF chunk is set, the encryption key is token3, that is the encrypted H4 (handshake) token (see chap. 4). Because token3 is part of the connection state block, changing the user password in the user table has no effect for the existing connections. Some remarks to the possibility of a man-in-the-middle-attack: Even he does not know the (old) password, he can try to manipulate the message with the intention to foist a wrong new password to the server. If the server accepts this manipulated password the client is locked out. But: While using the Rijndael ciphering algorithm [AES], changing only one bit of the message results in a complete chaos when decoded (The whole message is involved because the A1 message consists of one complete and a shorten block so cipher text stealing come into Wildgrube Informational [Page 14] SDXP: Structured Data Exchange Protocol October 2003 effect). In this case the server rejects this message and disconnect. The client is than informed that his request is unsuccessful. So he can continue with his old password. The fact that messages are manipulated may be more obviously by switching on the use of message digests. Wildgrube Informational [Page 15] SDXP: Structured Data Exchange Protocol October 2003 9. IANA Considerations A specific installation or an extension of SDXP can use additional chunks for handshaking. Applications with these extensions are compatible with SDXP if the applications follows the [SDXF] main rule: "IGNORE UNKNOWN CHUNKS". New chunk ID's which realize a real extension of SDXP should be registered by IANA. Following item is registered by IANA: CHUNK ID's FOR SDXP For the handshake messages a set of SDXF chunk ID'S are used to build these messages. Taking into account that some programming languages cannot handle unsigned integers these values ranges between 1 up to 32767 as 2-Byte integers. These numbers are assigned by IANA and listed here: chunk ID constant name Description ----------- ------------- -------------------------------- 1-31999 private or application specific 32000-32749 IANA to assign top level IDs: 32750 ID_MESSAGE user message (payload) 32751 ID_CONNECT connect message 32752 ID_DISCONNECT disconnect message 32753 ID_ADMIN administration message 32754 ID_AUTH authentication message 32755 ID_HSHAKE handshake message elementary IDs: 32759 ID_DIGEST message digest 32760 ID_TOKEN security token 32761 ID_OPTION additional parameter 32762 ID_STATUS server status: READY or QUIET 32763 ID_BUFFSIZE senders buffer size 32764 ID_CONNID handshaking: connect ID 32765 ID_USERID user ID 32766 ID_PASS authentication: Password 32767 ID_ACK acknowledgment Hints for assigning a number: Developers which want to register new chunk ID's for handshake messages for SDXP should contact IANA. The ASSIGNED NUMBERS document should be referred to for a current list of chunk ID's, see [IANA]. Wildgrube Informational [Page 16] SDXP: Structured Data Exchange Protocol October 2003 Appendix Example: Realizing a RPC protocol. This will demonstrate how to define a RPC protocol with the means of SDXP. At first we state that this protocol is based on SDXP. As a consequence all messages are SDXF chunks with ChunkId ID_MESSAGE. We have to define the structures of the request and response messages: The Request (or Call): (MESSAGE) | +--(MODULE:n) +--(VERSION:n) +--(PROCEDURE:n) +--(PARAMETERS) | +--(PARAM=param-1) +--(PARAM=param-2) +--(PARAM=param-3) : +--(PARAM=param-n) The Response: (MESSAGE) | +--(RESPONSE_CODE:n) +--(RETURN_VALUE) -- Type is Module/Procedure specific -- (may be a structure too) These definitions are following those in [SUN], referring chapter 8, the term "program" (prog) is replaced here by "module". To complete the definition we have to define the numerical values of the stated ChunkID's (ID_MODULE, ID_VERSION, etc.) and to give a concept for the response codes. This will not worked out here. That's all, apart from some special issues. All other chapters in [SUN] will be covered by SDXP or [SDXF]. Wildgrube Informational [Page 17] SDXP: Structured Data Exchange Protocol October 2003 References 1. Normative References [AES] U.S. DEPARTMENT OF COMMERCE/National Institute of Standards and Technology (NIST), "ADVANCED ENCRYPTION STANDARD (AES)", FIPS PUB 197, November 2001, http://csrc.nist.gov/encryption/aes/ [IANA] Internet Assigned Numbers Authority, http://www.iana.org/numbers.htm [SDXF] Wildgrube, M., "SDXF: Structured Data Exchange Format", RFC 3072, March 2001. [SHA-1] U.S. DEPARTMENT OF COMMERCE/National Institute of Standards and Technology, "Secure Hash Standard (SHA-1)", FIPS PUB 180-1., April 1995. 2. Informative References [ESRO] Banan, et. al, "AT&T/Neda's Efficient Short Remote Operations (ESRO)", RFC 2188, September 1997 [SCHNEIER] Bruce Schneier, Applied Cryptography, 2nd edition. [SOCKS] Leech, et al, "SOCKS Protocol Version 5", RFC 1928, March 1996 [SUN] Srinivasan, R., "RPC: Remote procedure call protocol specification version 2". RFC 1831, Sun Microsystems Inc, August 1995. [VMTP] Cheriton, D., "VMTP: VERSATILE MESSAGE TRANSACTION PROTOCOL", RFC 1045 , Stanford University, February 1988. [XEROX] Birrel, A. D., and Nelson, B. J., "Implementing Remote Procedure Calls", XEROX CSL-83-7, October 1983. or: ACM Transactions on Computer Systems, 2(1):39--59, February 1984. Author's Address Max Wildgrube Schlossstrasse 120 60486 Frankfurt Germany email: max@wildgrube.com For a detailed description of an implementation of SDXP see: http://www.pinpi.com Expiration Date: April 2004 Wildgrube Informational [Page 18]