____ _________ __ ___ ____ UUCP Interface to VMS MAIL Kevin Carosso Hughes Aircraft Co. Space and Communications Group PO Box 92919 Building S-50/X303 Los Angeles, Ca. 90009 April 13, 1985 Introduction This file documents the UUCP foreign protocol interface to the VMS MAIL system. This file is not a guide to writing your own interface for some other protocol, but describes the interface as it relates to my code for the UUCP network. Together with NOTES.TXT and the UUCP_MAIL.PAS source code, however, this should help you to convert this code to your own uses or figure out how to create your own from scratch. In addition to providing a working example of a VMS MAIL foreign protocol, this package provides a parser for handling RFC-822 conformant message headers. The parsing tables and logic for this may also prove useful to someone writing a foreign mail interface which must understand such headers. Note: The foreign protocol interface within MAIL is undocumented at this time. It may change without notice in a future release of VMS. The information necessary to write this code comes from the MAIL source on the VMS microfiche. The most useful information is the routine NETJOB in module MAIL$MAIL (230-E2), which handles incoming foreign mail, and the various routines in module NETSUBS (230-N11), most of which deal with outgoing foreign mail. Using the UUCP Interface A UUCP address is indicated within MAIL by any address of the form: uucp%"any-uucp-address-path" or gateway::uucp%"any-uucp-address-path" where "gateway" is the name of a DECnet node serving as a UUCP gateway. Note that the quoted string is required since UUCP host and user names are case-sensitive and contain "!"'s. With the UUCP protocol module, this form of address can be used in distribution lists, forwarding entries, and logical names just as any other address may appear. Page 2 Incoming UUCP messages will appear to come from addresses of the same form, so you can use the REPLY command in MAIL to reply to UUCP messages. Installing the UUCP Interface UUCP_MAILSHR.EXE is the shareable image that is invoked by MAIL when it sees a "UUCP%" address. Due to some brain-damage within MAIL, the mechanism allowing the file to be referenced by the logical name MAIL$PROTOCOL_UUCP will not work. Therefore, the image must be called UUCP_MAILSHR.EXE and reside in SYS$SHARE:. Since this file is a shareable image that is invoked by a privileged image (MAIL.EXE), it must be made a known image using the VMS INSTALL utility. It should, therefore, be owned by [SYSTEM] and not [UUCP]. Note: In general, [UUCP] should not be considered a "trusted" user, since it may be possible for someone to convince UUCP to modify or delete files that it has access to. To minimize the damage, files that are installed or referenced by system processes (such as the command file UUCP_ROOT:[LIB]STARTDAEMON.COM that starts up the UUCP daemon process) should be owned by [SYSTEM] and not by [UUCP]. RMAIL.EXE is run by UUCP whenever received UUCP mail is to be delivered. RMAIL.EXE must reside in UUCP_ROOT:[EXE]RMAIL.EXE. RMAIL spawns a subprocess that references a command file that actually invokes MAIL to deliver the message. Because mail delivery on VMS requires the privileges SYSPRV and DETACH, RMAIL.EXE must be installed with these privileges. In order that no other users be allowed to run the privileged RMAIL, the executable file is owned by [SYSTEM] and protected with an ACL to allow execute only access to [UUCP] and no access of any kind to any other non-system UIC. The command file used by RMAIL is UUCP_ROOT:[LIB]UUCP_MAIL_PROTOCOL.COM. Since this command file is run in a privileged context, care must be taken to insure that the command file cannot be modified or overwritten by non-privileged users. This file is protected the same as RMAIL.EXE. Page 3 A directory listing of the relevant files is: Directory UUCP_ROOT:[EXE] RMAIL.EXE;30 49/51 9-APR-1985 17:07 [SYSTEM] (RWED,RWED,RE,) (IDENTIFIER=[UUCP],OPTIONS=PROTECTED,ACCESS=EXECUTE) Directory UUCP_ROOT:[LIB] UUCP_MAIL_PROTOCOL.COM;3 1/3 9-APR-1985 17:06 [SYSTEM] (RWED,RWED,RE,) (IDENTIFIER=[UUCP],OPTIONS=PROTECTED,ACCESS=EXECUTE) Total of 3 files, 96/102 blocks. The UUCP MAIL protocol module conforms to the RFC-822 standard for internet message headers. Part of the standard requires that the "Date:" header include the time zone with the system time. Since VMS has no understanding of time zones, this information is provided through a system logical name, MAIL_TIME_ZONE. The equivalence string for this logical name is simply the text of the time zone you wish to use. RFC-822 (included in this package as the file RFC822.TXT) defines legitimate values for the time zone. You should remember to change this logical name whenever you change the system time zone (eg. from daylight savings to standard time). SYSTARTUP.COM should be modified to create the required logical names and known images for the UUCP protocol module whenever the system boots. An example entry for SYSTARTUP.COM would be: $ ! $ ! Definitions for network MAIL... The time zone should be changed when $ ! changing between PDT and PST!!! $ ! $ define/system/exec MAIL_TIME_ZONE PDT $ ! $ ! Install required images $ ! $ mcr install UUCP_MAILSHR/OPEN/SHARE/HEADER_RESIDENT UUCP_ROOT:[EXE]RMAIL.EXE/PRIV=(SYSPRV, DETACH) $ ! Note that these entries are in addition to entries required for UUCP itself. You should define all UUCP logical names first. Then do the entries above for UUCP mail, and then (last) actually start up the UUCP daemon process. This insures that the necessary environment is set up before UUCP actually tries to send or receive files or mail. UUCP_MAILSHR uses a UUCP scratch directory to place outbound message text and the associated commands for delivering the message. The directory is UUCP_ROOT:[SPOOL.UUCP.XTMP]. In order that message files be accessible and deletable by UUCP but not readable by normal users, the directory has a default protection ACL placed on it. Also, so that files created there can be owned by [UUCP], (so users don't need Page 4 disk quota entries on that device) the files are created with SYSPRV enabled. I did, however, restrict the logical name search to EXEC mode, so a user cannot create a logical name to confuse MAIL and attempt to get it to write the files somewhere else while it has SYSPRV on. This means that the logical name UUCP_ROOT must be defined in EXEC mode. Directory UUCP_ROOT:[SPOOL.UUCP] XTMP.DIR;1 1/3 12-FEB-1985 17:39 [UUCP] (RWE,RWE,RWE,RWE) (DEFAULT_PROTECTION,SYSTEM:RWED,OWNER:RWED,GROUP:,WORLD:) (IDENTIFIER=[UUCP],OPTIONS=DEFAULT,ACCESS=READ+DELETE) Total of 1 file, 1/3 blocks. If any files of the form UUCP_MESSAGE.* or UUCP_COMMAND.* end up in the XTMP directory and are not immediately used and deleted by UUCP, you can assume that they are a message which for some reason never got sent out of the system. You can use the FLUSHMAIL.COM command procedure to hand the messages to UUCP for delivery. They will be deleted if they are successfully sent by UUCP. In this distribution of UUCP, FLUSHMAIL.COM is located in the root of the tree, with CREDIR.COM. It should be moved to [SPOOL.UUCP.XTMP] after CREDIR creates this directory for you. Implementation Notes The following notes are extracted from the comments of UUCP_MAIL.PAS. They are provided here simply to give some clue to interested parties of why UUCP_MAIL does some of the things it does. UUCP_mail is invoked with a call to LIB$FIND_IMAGE_SYMBOL in MAIL. The shareable image containing this code must be placed in SYS$LIBRARY:UUCP_MAILSHR.EXE. Incoming mail messages are handed to the VMS MAIL system through this module with a command of the form: $ mail/protocol=UUCP_MAILSHR message.txt addressee To route UUCP mail through this node, "addressee" is simply a UUCP address of the form: uucp%"uucp-path". I think that "addressee" could also be a list of people to take delivery, but UUCP does not use such an address. There is a constant called DEBUG_ON which determines whether UUCP_MAIL should write out debug information. This is currently enabled for incoming messages. The output goes to PAS$OUTPUT, which is normally SYS$OUTPUT, which is set by RMAIL.EXE to be the file UUCP_ROOT:[SPOOL.UUCPPUBLIC]RMAIL_MAIL.LOG. "ARPA_date_time" returns a string which is the current system date and time in the syntax specifed in RFC-822 under "Date and Time Specification". Note that the zone field is gotten by translating the Page 5 logical name MAIL_TIME_ZONE. If we get any sort of error doing the translation, we assume it's not available and do not include a time zone. Note that this will work, but that the time zone is a required portion of the date-time specification. "Read_headers" reads headers from a file into a header structure. We can handle an (essentially) infinite number of headers, since we put them into a linked list. TPARSE should return a syntax error if we scan something that isn't really a header, so we don't have to worry about scanning too far into the message body if there are no headers or if they (illegally) run into the body. One possible problem is that our lines can not be infinitely long. Depending on the mechanism used to read the file, we either truncate or get an error on anything over MESSAGE_LINE_MAX characters long. I should get rid of the Pascal I/O and just use RMS for this. Sometimes the thing can abort with the bogus error message "Required recipients parameter not found on command line" (or some such). I do not know WHY this is the error it bombs out with, only that it generally means that Pascal I/O is dying on a record structure it does not want to read. I'm fairly certain I've fixed all Pascal I/O that could've caused this, but beware if you convert this to some other protocol. If you start seeing this error message and you know it's not something you left off the command line, use the debuggable MAIL.EXE (I describe how to patch this in my NOTES.TXT file). If you do a SHOW CALLS in DEBUG after this happens, it'll give you the real error message. I think the problem is that MAIL_IN_CONNECT gets called as a condition handler sometimes. The message may contain headers in several different forms. There may be no headers at all, RFC-822 headers only, UUCP headers only, or UUCP headers followed by RFC-822 headers. The last case is what we'll usually see (from UNIX systems running sendmail or from VMS systems running this code). UUCP only headers may be coming from a System V or old Berkeley UUCP. We have to handle the case of no headers as well, just in case. I doubt we'll ever see only RFC-822 headers, but may as well check for them while we're going through with all this bullshit. To handle this, we have a simple state machine that moves between the states (no_headers, rfc_822_headers, UUCP_headers) based on the kind of header lines we see. The state "rfc_822_headers" is also used for the case where there are UUCP headers as well as RFC-822 style headers. While we are reading them in, we spot any "To:", "From:", "From ", and "Subject:" lines for later use, so we can get at them without rescanning the list. The first header we spot of each of these is the one we'll use for that function. By the way, I only bother with leading UUCP lines. Once I see an RFC-822 line, any subsequent UUCP lines are ignored. (wish I could ignore the bloody things completely!) When reading the headers from incoming messages, we first remove all UUCP style header lines and then add a "Received:" header to the top Page 6 to show the message has come through us. Here's how we handle message headers in outgoing messages: First see if there are any headers in the incoming text. If so, then we are probably routing a message through here (or someone wanted to put their own headers into the file). If not, then build some default headers from scratch. This means that a user may type headers into his outgoing text and they will be seen and honored, to a certain extent, by the UUCP protocol code. I am not certain of all of the ramifications of this. Note that if we are routing a message through the system to another system, our _IN_ code has been run. If this is the case, then the _IN_ code has put a "Received:" header line in the message, whether or not the message originally had RFC-822 headers. Since this code sees the "Received:" line it'll think the message has headers even if it came from a system which did not make headers. This cleverly causes the code here to NOT make up headers on files being routed through, even if they didn't come with headers originally. When making up headers, we have the following caveats: Whatever "From" information comes out of the headers is overridden by the "From" address we are given by MAIL. This is so people cannot forge the from address. When I make up my own "To:" line, I put the address that I think this message is going to in it. This is because I know that is a legal address. The "To" line that VMS MAIL hands me is simply what the user typed, and may be an indirect file, logical name, or who-knows-what. I cannot put that into the "To:" line because some mailers who receive this message may want to parse that for replies. Hence, I am making up my own header field called "X-VMS-Mail-To:" so that recipients can see what the original to line was. Next we mangle the headers to indicate that the message is being sent out from this system. At this point it matters not whether the headers we are mangling were built in the code right above or are something the message came into our system with. We do the following: - Strip out all OLD_UUCP_FROM headers. - Replace the "From" address fields with our UUCP_HOST_NAME on the front of whatever MAIL said the from address was. Bugs There are currently two bugs in the UUCP MAIL interface. The code is currently not smart enough about recognizing messages routing through the system, which come in with headers and then go out with those headers, and messages which start here and need headers to be manufactured. Currently, if a message going out looks like it has headers, we decide it must've come in with them, and so we treat it Page 7 like something being routed through. Unfortunately, some outgoing message can look like they have a header at the beginning and fool this. Not much harm is done, the message is sent, but it does not have the headers created from scratch that it should have. The second bug is the fact that we need to be more intelligent about rewriting addresses into a cononical (legal as per RFC-822) form. Currently, this code works just fine as a gateway between the DECnet world and the UUCP world, mixing DECnet and UUCP addresses correctly and routing between the nets correctly. Unfortunately, DECnet addresses are not legal RFC-822 addresses, so things get confused at strict 822 sites if we send a message out that came from a DECnet sender. What's needed is a file that indicates what host is on what net so we can convert between "host::user", "host!user", and "user@host" as needed.